while developing in Xcode it is common to switch between Debug and Release mode and using some parts of code in Debug mode only while not using some in Release mode.
I often throw out NSLog
code by some #define
rule that lets the Pre-compiler parse out those commands that are not needed in a Release. Doing so because some final testing needs proof everything works as expected and errors are still handled properly without messing some NSLog i possibly forgot. This is in example of importance in audio development where logging in general is contra productive but needed while debugging. Wrapping everything in #ifdef DEBUG
is kinda cumbersome and makes code lock wild, so my #defines are working well to keep code simple and readable without worrying about NSLog
left in releases while still Logging on purpose if needed. This praxis works really well for me to have a proper test scenario with pure Release code.
But this leads to compiler warnings that some variables are not used at all. Which is not much of a problem but i want to go one step ahead and try to get rid of those warnings also. Now i could turn those warnings off in Xcode but i try to find a way to keep those and just getting rid of them for my NSLog overruled #defines
So instead of logging against dev>null
i throw out (nullify) all code that is wrapped by NSLog(
... )
and use some extra defined rule called ALLWAYSLog()
that keeps NSLog in Releases on purpose and also changes NSLog to fprintf to avoid app origin and time prints.
Here my rules..
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr, "%s \n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String])
#else
#define NSLog(FORMAT, ...) {;}
#endif
#define ALLWAYSLog(FORMAT, ...) fprintf(stderr, "%s \n", [[[NSString alloc] initWithFormat:FORMAT, ##__VA_ARGS__] UTF8String])
To get rid of those unused variable warnings we often use
#pragma unused(variablename)
to inform the precompiler we did that on purpose..
Question:
Is it possible to write some #define
rule that makes use of #pragma unused(x)
?
Or how to integrate this mentioned way of __unused
attribute
EDIT: with all your help and the nicely working solution of @dbush below my #define rules would look like this at the moment..
#define ALLWAYSLog(...) fprintf(stderr, "%s \n", [[NSString alloc] initWithFormat:__VA_ARGS__].UTF8String)
#ifdef DEBUG
#define NSLog(...) ALLWAYSLog(__VA_ARGS__)
#else
#define NSLog(...) {0 && ALLWAYSLog(__VA_ARGS__);}
#endif
EDIT2: see answer below.
CodePudding user response:
In the #else
case, you can put the function call on the right side of the &&
operator with 0 on the left side. That will ensure that variables are "used" while also ensuring that the function doesn't actually get called and that the parameters are not evaluated.
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr, "%s \n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String])
#else
#define NSLog(FORMAT, ...) (0 && fprintf(stderr, "%s \n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]))
#endif
CodePudding user response:
after testing and still not believing there is no "official way" of doing this i ended up reading my header files again. (usr/include/sys/cdefs.h
)
where __unused
is declared as __attribute__((__unused__))
.
This seems the officially way of telling the Apple Clang (GCC) compiler a specific variable will be not used intentionally by placing a __unused
directive at the right place in code. In example in front of a variables declaration or after a function declaration and more.. see stackoverflow discussion starting 2013 ongoing
@dbush 's answer was and is nice because it suppresses the unused variable warning by making use of the passed arguments and introducing nullify logic that will do no harm - but will still be executed. That was pretty close to my goal but backfires as (sadly) more code ends up in Release instead of less code.
in example:
#define NSLog(...) (0 && fprintf(stderr,"%s",[[NSString alloc] initWithFormat:__VA_ARGS__].UTF8String))
// applied to
int check = 333;
NSLog(@"findMeAgainInPreProcess %d",check);
// preprocesses to
int check = 333;
{0 && fprintf(__stderrp,"%s \n",[[NSString alloc] initWithFormat:@"findMeAgainInPreProcess %d",check].UTF8String);};
While this will not print anything, this clearly executes even more than just plain NSLog() would do without altering it via macro. Guessing the compiler will not be clever enough to know my "unuse" intention here.. Specially because the used variable is declared above and i get the warning again for good reason then, i introduced code that declares a lot and does execute a lot and does nothing.
Well, this lead me to a more reciprocal approach making use of __unused
again in combination with my first approach like so...
#define ALLWAYSLog(...) fprintf(stderr,"%s \n",[[NSString alloc] initWithFormat:__VA_ARGS__].UTF8String)
#ifdef DEBUG
#define IN_RELEASE__unused
#define NSLog(...) ALLWAYSLog(__VA_ARGS__)
#else
#define IN_RELEASE__unused __unused
//#define NSLog(...) NSCAssert(__VA_ARGS__,"")
//#define NSLog(...) {;}
#define NSLog(...) /*__VA_ARGS__*/
#endif
With this i introduce the __unused
directive in different ways for Debug and Release. In Debug it will not silence the warning by parsing out the directive itself and in Release it will exchange IN_RELEASE__unused
to __unused
according to the macro.
I could have used NSCAssert(__VA_ARGS__,"")
as macro for NSLog(...) in Release ending up with do {} while(0);
in exchange doing nothing also. But {;}
is even simpler doing same - nothing.
As __unused
marks properly then the macro parser is clever enough to comment passed arguments out of scope with /*__VA_ARGS__*/
. This later variant clearly gets kicked out from the compiler after preprocessing leaving only ;
as all comments get kicked too.
Means this simplifies notation like below..
IN_RELEASE__unused int check = 333;
NSLog(@"findMeAgainInPreProcess %d",check);
// produces for DEBUG
int check = 333; //no warning, var is used below
fprintf(__stderrp,"%s \n",[[NSString alloc] initWithFormat:@"findMeAgainInPreCompile %d", check].UTF8String);
// produces for RELEASE
__attribute__((__unused__)) int check = 333; //no warning intentionally
; // no print, nothing
This keeps NSLog in place, marks the unused variables to silence the warning properly and NSLog gets parsed out completely.
And still, i believe this could be improved.. :)