4
\$\begingroup\$

I'd like to introduce a way to turn off/on debug messages and in-game tooling (tweak menus, for example) without non-game related branches, e.g:

if(DEBUG_MODE) {
    // show tweak menu

    // some message
    console.log("In this function");
}

Is there a more reusable approach to this problem?

\$\endgroup\$

6 Answers 6

2
\$\begingroup\$

The best bet, taking into account performance considerations, is to simply have a means of wrapping up debug code in a way that can be "compiled out" as best as possible, and then left in as run-time selectable if not compiled out.

In C/C++ this generally means a macro (or a set of macros for different specific needs) that wraps calls to debug functions that internally check debug flags. In a language like C#, you can use the [Conditional()] attribute and the run-time debug flags internally.

In languages like JavaScript, which it appears you might be using, you can fall back on good ol' functional composition for the run-time behavior, and simply including different .js files for the "release mode" vs "debug mode" implementation.

Note that this is not object-oriented. I don't think you're actually looking for an object-oriented approach at all, though.

Consider a debug script like so:

// debugtools-release.js
function debug(expr) {}

// debugtools-debug.js
function debug(expr) {
  if (window.debug_on && expr instanceof Function)
    expr.call(this);
};

You can now wrap up any debug logic you want like so:

do_something();

debug(function(){
  console.log("Did something");
  graphics.drawDebugLine(foo.x, foo.y, bar.x, bar.y, Color.Yellow);
  ++some_debug_counter;
});

do_something_else();

All of that debug code will be skipped if debugging is turned off. Better, modern JS compilers might completely remove any overhead involved, giving you similar performance to using macros in C++. (I'm not sure if they do or not, you'd have to test to be sure.)

For simpler cases, like just printing out a console log message, you can of course create helpers for those cases. Something like:

// debugtools-release.jse
function debug_msg(msg) {}

// debugtools-debug.js
function debug_msg(msg) {
  if (window.debug_on)
    console.log.apply(this, arguments);
};

If you're of the mind, you could combine both the debug and debug_msg functions by checking argument types and the like (though I prefer having clearly differentiated functions for differing behavior myself):

function debug(expr) {
  if (window.debug_on) {
    if (expr instanceof Function)
      expr.call(this);
    else
      console.log.apply(this, arguments);
  }
}

The general functional composition technique works equally well for other languages that lack explicit compiler support for optionally disabling code and which have lambda/anonymous functions of some form. I've even used it in C++11 code, though I'm still unsure whether I prefer that or the macro approach (and for games, since we generally have to support older compilers, the C++11 approach is just not yet feasible).

\$\endgroup\$
2
\$\begingroup\$

Well, as a first attempt you can move branching to debug objects:

Log: function (msg) {
    if (DEBUG_MODE) {
        console.log(msg);
    }
}

// and then use:
Log("In this function");

Not sure how well this approach can integrate with your "tweak menu" however.

\$\endgroup\$
2
\$\begingroup\$

If you use a ServiceLocator you could use a preprocessor flag to return either the debugging service or the production service.

See: http://gameprogrammingpatterns.com/service-locator.html#design-decisions (Bind to it at compile time).

The other method I know from non-games is Petr Abdulins way of having a Logging class where you can bind output devices (File, Console, Monitor, Nowhere) or logging filters (levels) to, so they can ignore messages if desired.

They would be used something like this, but probably my syntax is not totally correct.

Logger logger = new Logger();
logger->addOutput(new ScreenDevice());
logger->addOutput(new FileDevice(new LevelFilter(Level::WARNING)));

You would then either pack them into a Registry (globally available) or they come with Singleton or an own static function to retrieve multiple loggers like this:

Logging::getLogger('myfirstlogger');  // these could be used instead of new Logger()
Logging::getLogger('mysecondlogger'); // and when a Logger is already available
                                      // return that one

The problem I see with this is, that you always ship it with your compiled program, so you always have this call to a function that does nothing. Yet, it’s possible that compilers might optimize this so that the unneeded call would be deleted.

\$\endgroup\$
1
\$\begingroup\$

Why so object-oriented? What's wrong with a flag?

You can define flags simply by creating a new variable: DebugFlags = DEBUG_GAME | DEBUG_TOOLS; where DEBUG_GAME = 1<<0 and DEBUG_TOOLS = 1<<1 etc.

This would require adding a parameter to the Log function like this: function Log( message, flags ) and having all irrelevant messages disabled at startup, this way: if( flags & DebugFlags == 0 ) return;.

Assuming that you do all your logging from a single thread (each thread should have its own logging state anyway), you can at some point just store the old flags, assign new flags and execute some code, after which you set the flags back to the old ones.

P.S. Object-oriented = less reusable on the scale of all reusable things. Functions/data are above OO and algorithms are above both.

\$\endgroup\$
4
  • \$\begingroup\$ The problem with flags are, that they'll most likely require an environment variable or global - I want to avoid additional dependencies in my classes \$\endgroup\$ Nov 30, 2012 at 14:13
  • 1
    \$\begingroup\$ @MarkGiaBaoNguyen They have to be in at least the same access level as the Log class/function. I don't think that's something you could avoid doing things any other way. \$\endgroup\$
    – snake5
    Nov 30, 2012 at 14:25
  • \$\begingroup\$ I see your point, but my debug functions aren't limited to just log functions. A whole stack of debug tools will be used - in-game tooling, draw positions, sprite locations. I'm trying to avoid a maintenance nightmare. \$\endgroup\$ Dec 4, 2012 at 13:02
  • \$\begingroup\$ Since when is adding ~10 extra lines per source file a maintenance nightmare? I don't see a problem here. At least not one that could be solved in a way that increases efficiency of work. \$\endgroup\$
    – snake5
    Dec 4, 2012 at 13:11
1
\$\begingroup\$

Using the Component Based approach (c#) i have created a debug object that gets added as component to the Overall solution it is able to handle all debug requests that i'm interested in and draw them to screen by passing an object to the handler

i have then wrapped anything that it does within the compiler if statements

#IF DEBUG
#ENDIF

therefore if i add a peice of text to the Debug handler

i.e

public void AddTouchLocation(vector2 location , string text)
{
    #IF DEBUG        
    Dosomething(location,text);
    #ENDIF
}

the code doesn't do anything with it

i can also wrap the draw method like this also

whilst it maynot be the cleanest solution (it leaves a empty component in release mode) it does allow me to ignore any if debug anywhere else in the project.

public override void Draw()
{
    #IF DEBUG
     //Perform draw of touchlocations
    #ENDIF
}
\$\endgroup\$
0
\$\begingroup\$

When you would like to solve it in the most unnecessary, gratuitous, by-the-book OOP way possible, you would do it like that:

Define an interface (or abstract base class, when your programming language doesn't support interfaces) for everything you would usually have a class for. Have two implementing classes for each: one debug class and one release class. The debug class either extends the corresponding release class or works as a proxy for an instance of it. The methods of the debug class would output the debugging information and then call the corresponding method of the release class instance.

No debug and release objects are created directly. Instead of that, you have factory classes (or at least factory methods) which create release objects in release mode and debug objects in debug mode.

But seriously. There is nothing wrong with having some if (DEBUG)'s in your code or with a logging function which only outputs messages in debug mode. In one game project we had a logger function which also required a severity level for each logging call (DEBUG, INFO, WARNING, ERROR). A compilation switch determined the minimum log level which would be outputted in the log and the minimum which would be outputted on-screen.

\$\endgroup\$
2
  • \$\begingroup\$ OOP for maintainable code :) \$\endgroup\$ Dec 3, 2012 at 10:14
  • \$\begingroup\$ What you're stating here can be much more easily implemented in an OOP paradigm if you use AOP on top of it. You just have separate code for "business logic" and for debugging, and then you use some Aspect Oriented library (or built-in AOP if available in the language's platform). This allows for much better code re-use and flexible inter-weaving of the debug code in the desired places. This is not a feature of OOP per-se but I think that if you want to do it using an OOP language, this is the way to go. Also the debug overhead can easily be de-activated for production release this way. \$\endgroup\$ Dec 3, 2012 at 16:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .