10

I have a trace() macro I turn on and off with another macro, e.g.

#ifdef TRACE
    #define trace(x) trace_val(x, 0)
#else
    #define trace(x) 0
#endif

This generates warning: statement with no effect from gcc when I call trace() with TRACE undefined. After a little searching I found that changing

#define trace(x) 0

to

#define trace(x) (void)0

silences the error. My question is: Why? What's the difference?

blueshift
  • 6,742
  • 2
  • 39
  • 63

2 Answers2

9

The cast to void makes it clear that the programmer intends to throw the result away. The purpose of the warning is to indicate at that it's not obvious that the statement has no effect and thus it's useful to alert the programmer to that in case it was unintentional. A warning here would serve no purpose.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • That makes some sense. Is this a documented part of the language syntax? – blueshift Apr 05 '12 at 03:54
  • 1
    It's less about the language syntax than the particular compiler. Warnings aren't stipulated in the language, they're extras the compiler does to make things a bit easier for you. With or without the compier's warning, both syntaxes in the code are valid at the language level; they mean essentially the same thing, it's just that in one case it's more "obvious" (in a very informal sense) that it's intentional. – Edmund Apr 05 '12 at 03:58
  • _This_ warning isn't stipulated in the language. Some warnings are, but those are of the kind "this officially is an error, but I'll let it slip". – MSalters Apr 05 '12 at 07:35
7

The warning and the workaround are compiler-specific. What you can do however is the following:

#define NOP do { } while(0)

#ifdef ENABLE_TRACE
    #define TRACE(x) trace_val(x, 0)
#else
    #define TRACE(x) NOP
#endif

This avoids the underlying problem in the first place.

Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • Will the `{}` cause a problem with trailing semicolons? `trace(x); trace(y);` will end up looking like `{}; {};`... – cHao Apr 05 '12 at 03:25
  • @cHao: That's not the problem. It's `if (foo) trace(x); else trace(y);` The `{};` will end the `if` leaving the `else` hanging. – David Schwartz Apr 05 '12 at 03:25
  • That will leave `x` unused though, causing warnings if that's the only use of `x`. I like: `((void) (false && (x)))` – David Schwartz Apr 05 '12 at 03:27
  • @David: Clever. Why the void cast? – Niklas B. Apr 05 '12 at 03:29
  • 1
    To avoid the very warning the OP was asking about. ;) The `false` avoids evaluating `x`, assuming that's what's desired. (Your solution works too. In fact, I think I like it better.) – David Schwartz Apr 05 '12 at 03:29
  • @David: Ah, I was thinking about putting it inside the `while` condition. Would you like to remove your downvote? – Niklas B. Apr 05 '12 at 03:30
  • @David: I rethought this and came to the conclusion that if the `trace` macro is the only use of a variable, the warning is actually sensible and should not be surpressed. After all, what sense would it make to output the value of an unused variable? – Niklas B. Apr 05 '12 at 03:35
  • @Orgnl: That however poses the problem that it doesn't enfore the closing semicolon... Can you give an example where the `do/while` would have a different impact on the balancing then the function call `trace_val(x, 0)`? – Niklas B. Apr 05 '12 at 03:42
  • @Orgnl: Obviously the downside is that "historically some C++ compilers have refused to inline-expand any function containing a loop." As you noted, these times have passed. BTW, you can actually remove comments on SO :) – Niklas B. Apr 05 '12 at 04:17
  • Thanks about deleting comments, haha. This is what I get for reading/doing SO late at night. (By the way, I do have to compile with legacy compilers in one project I help maintain, so the habit of using if still works well for me). Argh, just posted this as an answer, I need to sleep. – std''OrgnlDave Apr 05 '12 at 04:24
  • @NiklasB. Actually, there is ONE advantage to the if () way of doing things. You can use single definitions. #define DO_IT 0 #define mymacro(x) if (DO_IT) x = 4; else (void)0 this way you can turn it on and off without a multi-line #ifdef thingy – std''OrgnlDave Apr 05 '12 at 04:32
  • I started to think that OP solution is best. Because `(void)0` you can place into expression itself and `do { } while(0)` you can not. For example- `(3,(void)0,7)` expression is perfectly valid, but `(3,do { } while(0),7)` it is not. Of course it is another question is it reasonable to put trace() calls into expressions itself. But for me `(void)0` wins :-) – Agnius Vasiliauskas Apr 05 '12 at 10:48
  • @0x69: There are two problems with `(void)0`. One is that complete garbage will compile, leading to a round of bugfixing each time you enable tracing. (If you remove/change a variable, you may not notice the trace step, so the tracing code can easily slip out of sync from the other code.) The other is that you'll get bogus "... is unused" warnings if you compute/save a value just for tracing. That's not terribly uncommon, IMO. – David Schwartz Apr 05 '12 at 20:30
  • For dismissing "is unused" warnings we can declare NOP trace part like this: `TRACE(x) (void)x` – Agnius Vasiliauskas Apr 06 '12 at 07:01