tags:

views:

367

answers:

5

Say I have a C++ function debugPrint(int foo). How can I most conveniently strip that from release builds? I do not want to surround every call to debugPrint with #ifdefs as it would be really time consuming. On the other hand, I want to be 100% sure that the compiler strips all the calls to that function, and the function itself from release builds. The stripping should happen also, if it's called with a parameter that results from a function call. E.g., debugPrint(getFoo());. In that case I want also the getFoo() call to be stripped. I understand that function inlining could be an option, but inlining is not guaranteed to be supported.

+12  A: 

Use conditinal compilation and a macro:

#ifdef _DEBUG
   #define LOG( x ) debugPrint( x )
#else
   #define LOG( x )
#endif

Define _DEBUG for the debug build and not define it for the release build. Now in release build every

LOG( blahbhahblah );

will be expanded into an empty string - even the parameters will not be evaluated and will not be included into the emitted code.

You can use any already existing preprocessor symbol that is defined in debug build and not defined in release instead of _DEBUG.

sharptooth
Yep .. thats exactly what macros are there for !!
Newtopian
This is dangerous and error prone. `int x = 0; LOG(x += 2); use(x);` will behave differently if "release" or "debug" mode is turned on.
LiraNuna
Yes, it will. Nothing comes for free in this world.
sharptooth
I would avoid putting the semi colon on the end of debugPrint and force the user to put iton the end of the LOG statement though. Otherwise problems WILL arise.
Goz
If you want to mitigate the problem LiraNuna pointed out try: #define LOG(x) (void)x in the else branch....
Dan O
That "mitigates" LiraNuna's problem but violates the initial requirement that LOG(GetFoo()) doesn't actually call GetFoo() in release mode. This is a boolean choice, and juvenis made his decision explicit in the question. So this answer is correct and should not be "mitigated".
MSalters
Fair point, but the example (both in the answer and the question) was debug print functions. LiraNuna's comment is worthwhile in that context.
Dan O
+1  A: 

I've done something like this before with the preprocessor.

#ifndef DEBUG
#define debugPrint
#endif

Essentially that will remove all the debug lines

Matt H
Err, wouldn't that turn the valid 'debugPrintf ("hello");' into the rather invalid '("hello");' ?
paxdiablo
@Pax, that's perfectly valid.
strager
But one that's likely to trigger a warning. `(void)("hello")` seems more appropriate. It's wrong, though: debugPrint(GetFoo()) will still call GetFoo() with this macro, which was explicitly not demanded.
MSalters
@strager, I stand corrected. I forgot about that particular misfeature of the language since I rarely use it (knowingly: obviously since 'i++' and `sprintf(...)` are similar, I use it quite a bit without thinking about the fact I'm ignoring the return value).
paxdiablo
Everyone does, all the time. std::cout << "hello" returns std::cout, after all, so you could write std::cout << "hello" << "world" << std::endl;
MSalters
+7  A: 

Make the function inline, and inside the function have the #ifdef's, like this:

inline void debugPrint(whatever_t wtvr)
{
    #ifdef DEBUG
        Logger::log(wtvr);
    #endif
}

That way the optimizer will strip the empty function while keeping the code clean.

LiraNuna
Will this guarantee that the function parameter will not be evaluated?
sharptooth
This won't help with the other requirement (that if there is code that needs to be evaluated to calculate wtvr won't be generated either)
Tal Pressman
@sharptooth, They have to be evaluated with this solution. However, if evaluation changes how the program operates (that is, side effects), there problem is that the calls to the logger function could do more than just logging.
strager
This is even smarter, since it lets the compiler choose whether to optimize away the call or not - if it's a function that returns data, it'll optimize away since pushing and poping from the stack is useless. if the function does modify something it will not be optimized out, as it should.
LiraNuna
+1  A: 

#ifdef _DEBUG
#define debugPrint(x) _debugPrint(x)
void _debugPrint(int foo) {
    // debugPrint implementation
}
#else
#define debugPrint(x)
#endif
Havenard
+2  A: 

@sharptooth's answer is good. One minor change that I normally make, however, is to disable debugging if the NDEBUG macro is not defined, rather than if a debug macro is defined:

#ifndef NDEBUG
   #define LOG( x ) debugPrint( x )
#else
   #define LOG( x )
#endif

When the NDEBUG macro is defined, then C asserts, which I tend to use rather judiciously for asserting things like preconditions and postconditions or even to help clarify the result of complex logic, drop out of the compilation as well. It's a standard macro for "no debugging". And, the NDEBUG macro should already be defined in release builds.

See: http://www.opengroup.org/onlinepubs/009695399/functions/assert.html

Daniel Trebbien