2

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 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
Community
  • 1
  • 1
Jean-Denis Muys
  • 6,772
  • 7
  • 45
  • 71

1 Answers1

1

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.

Jens Ayton
  • 14,532
  • 3
  • 33
  • 47
  • 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.) – Jens Ayton Sep 15 '10 at 13:51
  • 1
    This only works if only 1 argument is passed in, once two get passed in I still get the warning – Dan F Sep 05 '13 at 14:56