16

I found this while reading some source code.

 #define MACRO(x)  if((void) 0, (x)); else some_func();

I don't fully understand the reasons behind that operator comma and the void cast. This has probably something to do with macro protection, I know that (void)0 is used sometimes to protect cascading elses in macros such as in if(...) then foo(); else (void)0.

Any ideas of why operator comma is there?

edit: I'm starting to think this has something to do with the owl (0,0).

Community
  • 1
  • 1
Giovanni Funchal
  • 8,934
  • 13
  • 61
  • 110

3 Answers3

9

I would guess that the trick is used to prevent the user from declaring variables in the if condition. As you probably know, in C++ it is legal to do this

if (int i = some_func()) {
   // you can use `i` here
}
else  {
   // and you can use `i` here
}

The use of comma operator in that definition will prevent macro usage like

MACRO(int i = some_func());

and force the user to use only expressions as argument.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
4

The void conversion there is definitely to prevent calling an overloaded operator , since you can't overload with a void parameter. This guarantees that (void)0, has no effect.

Why the comma operator is there? A good question. I really don't know.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 3
    I don't understand. Can you explain in more detail what `((void)0,x)` is supposed to guard against that a simple extra pair of parentheses wouldn't `if((x)); else some_func();` ? – CB Bailey Oct 12 '10 at 15:33
2

This looks a bit as if somebody may have started with some code that included an assert, pre-processed it, and turned the result into a macro. When NDEBUG is defined, assert has to turn into nearly nothing -- but, syntactically, still has to produce some placeholder code. For example, you're allowed to use it in a situation like:

assert(x), *x = 1;

When you compile this with NDEBUG defined, it still needs to compile, but the assert shouldn't do anything. To support that, assert is typically defined something like this:

#undef assert
#ifdef NDEBUG
#define assert(x) ((void)0)
#else
#define assert(x) ((!!x) || __failassert(x, __FILE__, __LINE__))
#endif 

So, if somebody started with code like above, and then looked at the preprocessed version (with NDEBUG defined), they'd see something like:

((void *)0), *x = 1;

...and if they didn't understand the code very well, they might think that ((void)0) really meant/accomplished something.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Good reasoning, but I'm not sure that is the real reason. – Giovanni Funchal Oct 12 '10 at 15:27
  • I'm not *sure* it's the real reason either -- in fact, probably nobody but the original author can do anything but guess, and even he may not remember for sure either... – Jerry Coffin Oct 12 '10 at 15:30
  • this "crazy" works with original MACRO : #define CRAZY 0));if((1 – user396672 Oct 12 '10 at 15:35
  • @user396672, yes but is that the real reason? AndreyT's explanation sounds at least *halfway* reasonable, but this is working awfully hard to prevent something that it's hard to imagine anybody writing in the first place. – Jerry Coffin Oct 12 '10 at 15:45