views:

33

answers:

1

This comes out of my search for a smart variant of NSLog(). One key feature of BetterLog() is that the NSLog() replacement compiles out to nothing for release and distribution builds. The proposed solution (see eg http://stackoverflow.com/questions/300673/is-it-true-that-one-should-not-use-nslog-on-production-code) is define a preprocessor symbol to control the definition of BetterLog() depending on the kind of build. Typically:

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif

where DEBUG_MODE would be defined as a preprocessor symbol only for debug builds.

However, in a number of cases, namely when the logging statement was built with intermediary variables, the result is a compiler warning for unused variables. Here is an example:

if (error) {
    NSString *titleString = @"Error downloading thumbnail, will rebuild it";
    NSString *messageString = [error localizedDescription];
    NSString *moreString = [error localizedFailureReason] ? [error localizedFailureReason] : NSLocalizedString(@"Check the URL.", nil);
    BetterLog(@"%@: %@. %@", titleString, messageString, moreString);
} // silently ignoring *this* error is OK.

Here all three strings yield compiler warnings. And I hate compiler warnings.

Of course it's impossible to avoid without somehow conditionally including the variables declarations themselves. I made the following attempt, but it didn't work:

instead of simply defining DEBUG_MODE in debug mode only, I define it all the time, with value 1 in debug mode, and value 0 in release mode.

Then I tried to take advantage of the compiler dead code stripping optimization :

if (DEBUG_MODE && error) {
    // snip
}

The code is OK: it is correctly stripped out in release mode. Yet the compiler still emits the unused variable warnings.

So the question is: isn't it possible to do any better than the ugly:

#if DEBUG_MODE
if (error) {
    // snip
}
#endif
+1  A: 

One option would be:

#define BetterLog(...) do { (void)(__VA_ARGS__); } while (0)

This has the advantage that if you reach a BetterLog(), any side effects of its arguments will be evaluated, and it’s a clean statement so it’s not a bug to write if (x) BetterLog(@"%@", x); (which would break the next statement using your macro).

Personally, I prefer to use the “ugly” preprocessor approach, because it’s explicit about excluding the debug code.

Ahruman
What's the point of `while (0)`
Alexsander Akers
Alexsander, see question 10.4 of the C FAQ: http://c-faq.com/cpp/multistmt.html (Erratum: the bit about “non-standard `inline` keyword is a tad out of date, since it was standardized eleven years ago.)
Ahruman