Why Use Apparently Meaningless Do-While and If-Else Statements in Macros

Why use apparently meaningless do-while and if-else statements in macros?

The do ... while and if ... else are there to make it so that a
semicolon after your macro always means the same thing. Let's say you
had something like your second macro.

#define BAR(X) f(x); g(x)

Now if you were to use BAR(X); in an if ... else statement, where the bodies of the if statement were not wrapped in curly brackets, you'd get a bad surprise.

if (corge)
BAR(corge);
else
gralt();

The above code would expand into

if (corge)
f(corge); g(corge);
else
gralt();

which is syntactically incorrect, as the else is no longer associated with the if. It doesn't help to wrap things in curly braces within the macro, because a semicolon after the braces is syntactically incorrect.

if (corge)
{f(corge); g(corge);};
else
gralt();

There are two ways of fixing the problem. The first is to use a comma to sequence statements within the macro without robbing it of its ability to act like an expression.

#define BAR(X) f(X), g(X)

The above version of bar BAR expands the above code into what follows, which is syntactically correct.

if (corge)
f(corge), g(corge);
else
gralt();

This doesn't work if instead of f(X) you have a more complicated body of code that needs to go in its own block, say for example to declare local variables. In the most general case the solution is to use something like do ... while to cause the macro to be a single statement that takes a semicolon without confusion.

#define BAR(X) do { \
int i = f(X); \
if (i > 4) g(i); \
} while (0)

You don't have to use do ... while, you could cook up something with if ... else as well, although when if ... else expands inside of an if ... else it leads to a "dangling else", which could make an existing dangling else problem even harder to find, as in the following code.

if (corge)
if (1) { f(corge); g(corge); } else;
else
gralt();

The point is to use up the semicolon in contexts where a dangling semicolon is erroneous. Of course, it could (and probably should) be argued at this point that it would be better to declare BAR as an actual function, not a macro.

In summary, the do ... while is there to work around the shortcomings of the C preprocessor. When those C style guides tell you to lay off the C preprocessor, this is the kind of thing they're worried about.

do { } while(0) vs. if (1) { } in macros [duplicate]

Nope, you're not wrong.

There's actually a nice reason:

#define my_code if (1) { ... }

if (1)
my_code;

The problem is with the ; ! It shouldn't be there... and that would just look strange and not in the spirit of the language. You can either choose to have a code that expands in to two ; in a row, or a code that looks un-c-ish :)

On the other hand, the do-while construction does not have that problem.


Also, as others mentioned, there's an else problem:

if (1)
my_code;
else { ... }

Ignoring the ; issuse, the else block now belongs to the wrong if.

What's the use of do while(0) when we define a macro? [duplicate]

You can follow it with a semicolon and make it look and act more like a function.
It also works with if/else clauses properly then.

Without the while(0), your code above would not work with

if (doit) 
INIT_LIST_HEAD(x);
else
displayError(x);

since the semicolon after the macro would "eat" the else clause, and the above wouldn't even compile.

Significance of do{} while(0) [duplicate]

It can be used to leave a certain scope of code at any point without leaving the function. Consider:

do
{
beginAtomicOperationSequence();

ret = doSomething();
if (ret < 0) break;

ret = doSomething2();
if (ret < 0) break;

} while(0);

if (ret < 0) {
switch (ret) {
//handle error
}
rollbackAboveOperations();
} else {
commitAboveOperations();
}

There are some cases in which I would say that this is acceptable, particularly if one needs to make a sequence of operations which are to be considered atomic. Like in the example above.

C multi-line macro: do/while(0) vs scope block [duplicate]

Andrey Tarasevich provides the following explanation:

  1. On Google Groups
  2. On bytes.com

[Minor changes to formatting made. Parenthetical annotations added in square brackets []].

The whole idea of using 'do/while' version is to make a macro which will
expand into a regular statement, not into a compound statement. This is
done in order to make the use of function-style macros uniform with the
use of ordinary functions in all contexts.

Consider the following code sketch:

if (<condition>)
foo(a);
else
bar(a);

where foo and bar are ordinary functions. Now imagine that you'd
like to replace function foo with a macro of the above nature [named CALL_FUNCS]:

if (<condition>)
CALL_FUNCS(a);
else
bar(a);

Now, if your macro is defined in accordance with the second approach
(just { and }) the code will no longer compile, because the 'true'
branch of if is now represented by a compound statement. And when you
put a ; after this compound statement, you finished the whole if
statement, thus orphaning the else branch (hence the compilation error).

One way to correct this problem is to remember not to put ; after
macro "invocations":

if (<condition>)
CALL_FUNCS(a)
else
bar(a);

This will compile and work as expected, but this is not uniform. The
more elegant solution is to make sure that macro expand into a regular
statement, not into a compound one. One way to achieve that is to define
the macro as follows:

#define CALL_FUNCS(x) \
do { \
func1(x); \
func2(x); \
func3(x); \
} while (0)

Now this code:

if (<condition>)
CALL_FUNCS(a);
else
bar(a);

will compile without any problems.

However, note the small but important difference between my definition
of CALL_FUNCS and the first version in your message. I didn't put a
; after } while (0). Putting a ; at the end of that definition
would immediately defeat the entire point of using 'do/while' and make
that macro pretty much equivalent to the compound-statement version.

I don't know why the author of the code you quoted in your original
message put this ; after while (0). In this form both variants are
equivalent. The whole idea behind using 'do/while' version is not to
include this final ; into the macro (for the reasons that I explained
above).

do { ... } while (0) — what is it good for? [duplicate]

It's the only construct in C that you can use to #define a multistatement operation, put a semicolon after, and still use within an if statement. An example might help:

#define FOO(x) foo(x); bar(x)

if (condition)
FOO(x);
else // syntax error here
...;

Even using braces doesn't help:

#define FOO(x) { foo(x); bar(x); }

Using this in an if statement would require that you omit the semicolon, which is counterintuitive:

if (condition)
FOO(x)
else
...

If you define FOO like this:

#define FOO(x) do { foo(x); bar(x); } while (0)

then the following is syntactically correct:

if (condition)
FOO(x);
else
....

In C macros, should one prefer do { ... } while(0,0) over do { ... } while(0)?

Well, I'll go for an answer:

Is there a legitimate reason why one should prefer the second form of the macro... ?

No. There is no legitimate reason. Both always evaluate to false, and any decent compiler will probably turn the second one into the first in the assembly anyway. If there was any reason for it to be invalid in some cases, C's been around far long enough for that reason to be discovered by greater gurus than I.

If you like your code making owl-y eyes at you, use while(0,0). Otherwise, use what the rest of the C programming world uses and tell your customer's static analysis tool to shove it.

Understanding macro code statement with lambda and __attribute__

do...while(0) allows the macro to by called with semicolon at the end, i.e. macro(x,y,z); (note the semicolon at the end), that's an old trick back from C, you can look it up separately.

As for the rest... it defines a lambda (immediate function object) capturing everything by reference (not sure why) that throws exception of a given type with a given message and calls it immediately, effectively throwing an exception.

The attributes signify the lambda is unlikely to be called (cold) and prevent from inlining (noinline), see: https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html



Related Topics



Leave a reply



Submit