In many C/C++ macros I\'m seeing the code of the macro wrapped in what seems like a meaningless do while
loop. Here are examples.
#define FOO(X
@jfm3 - You have a nice answer to the question. You might also want to add that the macro idiom also prevents the possibly more dangerous (because there's no error) unintended behavior with simple 'if' statements:
#define FOO(x) f(x); g(x)
if (test) FOO( baz);
expands to:
if (test) f(baz); g(baz);
which is syntactically correct so there's no compiler error, but has the probably unintended consequence that g() will always be called.
Jens Gustedt's P99 preprocessor library (yes, the fact that such a thing exists blew my mind too!) improves on the if(1) { ... } else
construct in a small but significant way by defining the following:
#define P99_NOP ((void)0)
#define P99_PREFER(...) if (1) { __VA_ARGS__ } else
#define P99_BLOCK(...) P99_PREFER(__VA_ARGS__) P99_NOP
The rationale for this is that, unlike the do { ... } while(0)
construct, break
and continue
still work inside the given block, but the ((void)0)
creates a syntax error if the semicolon is omitted after the macro call, which would otherwise skip the next block. (There isn't actually a "dangling else" problem here, since the else
binds to the nearest if
, which is the one in the macro.)
If you are interested in the sorts of things that can be done more-or-less safely with the C preprocessor, check out that library.
I don't think it was mentioned so consider this
while(i<100)
FOO(i++);
would be translated into
while(i<100)
do { f(i++); g(i++); } while (0)
notice how i++
is evaluated twice by the macro. This can lead to some interesting errors.
The above answers explain the meaning of these constructs, but there is a significant difference between the two that was not mentioned. In fact, there is a reason to prefer the do ... while
to the if ... else
construct.
The problem of the if ... else
construct is that it does not force you to put the semicolon. Like in this code:
FOO(1)
printf("abc");
Although we left out the semicolon (by mistake), the code will expand to
if (1) { f(X); g(X); } else
printf("abc");
and will silently compile (although some compilers may issue a warning for unreachable code). But the printf
statement will never be executed.
do ... while
construct does not have such problem, since the only valid token after the while(0)
is a semicolon.
do {} while (0)
and if (1) {} else
are to make sure that the macro is expanded to only 1 instruction. Otherwise:
if (something)
FOO(X);
would expand to:
if (something)
f(X); g(X);
And g(X)
would be executed outside the if
control statement. This is avoided when using do {} while (0)
and if (1) {} else
.
With a GNU statement expression (not a part of standard C), you have a better way than do {} while (0)
and if (1) {} else
to solve this, by simply using ({})
:
#define FOO(X) ({f(X); g(X);})
And this syntax is compatible with return values (note that do {} while (0)
isn't), as in:
return FOO("X");
While it is expected that compilers optimize away the do { ... } while(false);
loops, there is another solution which would not require that construct. The solution is to use the comma operator:
#define FOO(X) (f(X),g(X))
or even more exotically:
#define FOO(X) g((f(X),(X)))
While this will work well with separate instructions, it will not work with cases where variables are constructed and used as part of the #define
:
#define FOO(X) (int s=5,f((X)+s),g((X)+s))
With this one would be forced to use the do/while construct.