How to avoid "unused variable" compiler warnings with conditionally compiled NSLog replacements?
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
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.
精彩评论