1

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

Ol Sen
  • 3,163
  • 2
  • 21
  • 30
  • There are inline pragmas in most compilers. You could also do `(void) var;`. – vandench Sep 22 '21 at 17:08
  • 1
    There is `_Pragma` (`_Pragma("unused(variable)")`), but is it going to work with e.g. `NSLog("%d", 1+1)`? – HolyBlackCat Sep 22 '21 at 17:09
  • Thank you for your response. @vandench your solution pretty much does the same what `{;}` already does but leads back to the warnings, or i am not clever enough to integrate it ^^. @HolyBlackCat yours tells me `_Pragma takes a parenthesized string literal` – Ol Sen Sep 22 '21 at 17:24
  • You're going to have to show how exactly you're implementing the `_Pragma`. Another option could also be to simply remove `FORMAT` from the macro. The varargs will consume all parameters, eliminating a specific variable to warn about. – vandench Sep 22 '21 at 17:28
  • @vandench good thoughts but the use of `...` is only valid with a leading variable which in case of NSLog is always FORMAT or what ever we name it. – Ol Sen Sep 22 '21 at 17:29
  • I'm not familiar with objective-c, but the requirement for a leading variable does not apply to macro arguments in C. https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html – vandench Sep 22 '21 at 17:30
  • I think I need to clarify, you can remove the `FORMAT` parameter from only the non-debug version. Because of the varargs it will accept what was traditionally the `FORMAT` parameter, as well as the print args, in the debug macro. This prevents you from running into compile time errors regarding incorrect parameter counts when not in debug, and should also remove unused warnings in non-debug mode. – vandench Sep 22 '21 at 17:37
  • @vandench nice hint.. which simplifies it to `#define NSLog(...) {0 && fprintf(stderr,"%s",[NSString stringWithFormat:__VA_ARGS__].UTF8String);}` applying dbushs answer. thank you – Ol Sen Sep 22 '21 at 17:47
  • Due to the `#`'s I'm pretty sure they have different functionality. Also I was intending a more simple solution where there wasn't an arguably ambiguous and useless print call in the macro; instead simply being replaced with nothing. The macro would eat all tokens passed in, and nothing would happen. It would simply be `#define NSLog(...)`. It's clear by looking at it, that it is not currently intended to do anything, a common idiom with debug macros in C that most programmers should easily recognize. – vandench Sep 22 '21 at 17:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/237376/discussion-between-ol-sen-and-van-dench). – Ol Sen Sep 22 '21 at 18:02

2 Answers2

1

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
dbush
  • 205,898
  • 23
  • 218
  • 273
  • tested yours.. it works well. Thank you so far, nice solution. Maybe someone comes up with even more simplifed define crazyness. I will wait a day and then make my green checkmark here i think. – Ol Sen Sep 22 '21 at 17:17
0

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 to find "Expression result unused". That was pretty close to my goal and is possibly still the most simple solution.

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, the compiler knows the expression is unused then.

But this left me with the question how unused marking is done properly. Trying a reciprocal approach distinguish Debug and Release to make 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(...) (0&&ALLWAYSLog(__VA_ARGS__)) //@dbush solution
//#define NSLog(...) NSCAssert(__VA_ARGS__,"")
//#define NSLog(...) {;}
#define NSLog(...) /*__VA_ARGS__*/
#endif

In Debug it will not silence the unused variable warning by parsing out the directive itself and in Release it will exchange IN_RELEASE__unused to __unused according to the macro and silence it. This is a little extra work but could help to see which parts are unused on purpose.

Means i can type 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 (in code), marks the unused variables to silence the warning properly and NSLog gets parsed out completely in Release. And i can still force prints for both modes with the introduced ALLWAYSLog.

Conclusion: dbush's solution is still more straight forward.

Ol Sen
  • 3,163
  • 2
  • 21
  • 30
  • 1
    While this would work, you still have to go through the effort of marking every variable that is only used in log statements. The `0 && fprintf(...)` method doesn't actually execute anything on the right side of the `&&` operator so there's no runtime penalty, although the code to call the function may still be present. However, a good optimizing compiler will see the constant 0 on the left side of `&&` and not even generate any code for what's on the right. – dbush Sep 23 '21 at 03:10