views:

507

answers:

7

Possible Duplicates:
What’s the use of do while(0) when we define a macro?
Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?
C multi-line macro: do/while(0) vs scope block

I have seen a lot of usages like this, previously I though that the programmer wanted to break out of a block of code easily. Why do we need a do { ... } while (0) loop here? Are we trying to tell the compiler something?

For instance in Linux kernel 2.6.25, include/asm-ia64/system.h

/*
 * - clearing psr.i is implicitly serialized (visible by next insn)
 * - setting psr.i requires data serialization
 * - we need a stop-bit before reading PSR because we sometimes
 *   write a floating-point register right before reading the PSR
 *   and that writes to PSR.mfl
 */
#define __local_irq_save(x)         \
do {                    \
    ia64_stop();                \
    (x) = ia64_getreg(_IA64_REG_PSR);   \
    ia64_stop();                \
    ia64_rsm(IA64_PSR_I);           \
} while (0)
+2  A: 

Looks like it's there just for scoping. It's similar to:

if (true)
{
    // Do stuff.
}

edit

I don't see it in your example, but it's possible that one of those function calls is actually a macro, in which case there's one key difference between do/while(0) and if(true), which is that the former allows continue and break.

Steven Sudit
If its just for scoping, why even have the 'do' and the 'while (0)' or in the case of this answer 'if (true)'? Why not just let the opening and closing curly braces stand on their own?
semaj
The answer is that it's not *just* for scoping. AndreyT and others have explained the rest of it.
Steven Sudit
+18  A: 

It's always used in macros so that a semicolon is required after a call, just like when calling a regular function.

In your example, you have to write

__local_irq_save(1);

while

__local_irq_save(1)

would result in an error about a missing semicolon. This would not happen if the do while was not there. If it was just about scoping, a simple curly brace pair would suffice.

OregonGhost
+1 That's a better explanation.
Steven Sudit
Fascinating. That's a handy trick!
Toji
Fascinating, kind of. I'd recommend an inline function over such a macro anytime (if you're using C++) though, which gets you the semicolon requirement for free :)
OregonGhost
@OregonGhost: Of course, this is from the Linux kernel. Have you noticed what Linus Torvalds thinks about C++?
David Thornley
I'd say that without the `if` example it doesn't really explain the issue with that `;`. We could have used a plain `{}` and the `;` would become optional. So what? So what if we forgot to put `;` somewhere where it is not really required? But once you see the issue with `if`, it becomes clear how the trick with `do/while` is better.
AndreyT
@David Thornley: I've read several things that Linus Torvalds thinks about some things, and in many cases I don't agree with him, so I don't care what Linus Torvalds thinks about C++. That doesn't mean that I would suggest using C++ for an operating system kernel ;)
OregonGhost
@AndreyT: It's true that the `if` thing is missing from my answer, but since Johannes Schaub wrote about that, I think I won't add it anymore. As I wrote in my other comment, I think requiring curly braces for every if leads to better code than hacking around that with such a macro trick. Note that requiring the semicolon even after a macro call makes the job easier for reading the code, and for IDEs, which likely expect a semicolon after a function call.
OregonGhost
@OregonGhost: I probably disagree with Linus about many things, C++ among them, but my point is that he does run Linux, so the Linux kernel is not where I'd expect to find C++ code.
David Thornley
@David Thornley: Well, I didn't say that one *should have used C++* for that snippet from the Linux kernel. I just suggested using inline functions instead if they are available, i.e. in C++ (or with several embedded C compilers which are typically not really standards-compliant anyway, for that matter). Note the "if you're using C++" condition in my first comment.
OregonGhost
@OregonGhost: Of course inlines are better than macros when you can use them. My point was that this was one of the least likely places to run into stuff that's not standard C90. It really wasn't all that important.
David Thornley
@David Thornley: ok, agree with that :)
OregonGhost
+13  A: 

It allows for the code to appear here:

if(a) __local_irq_save(x); else ...;

// -> if(a) do { .. } while(0); else ...;

If they simply used a { .. } you would get

if(a) { ... }; else ...; 

The else would not belong to any if anymore, because the semicolon would be the next statement and separate the else from the preceeding if. A compile error would occur.

Johannes Schaub - litb
Also a valid reason. On the other hand, I always use curly braces for control flow statements, so that this couldn't happen. A compiler error would therefore be acceptable for me. It would be worse if the else would be separated from the if *without* a compile error ;)
OregonGhost
OregonGhost: There isn't always a syntax error.`if( allowed ) if( ! appropriate ) __set_error(NOT_RIGHT_NOW); else __do_root_action( WIN ) ;`With `__set_error` macro using `do{...}while(0)`, this works, if you use just `{...}` the else is actually attached to the `if(allowed)` instead of the `if(!appropriate)`.Oops.
Michael Speer
@Michael Speer: Not true. If `__set_error` uses `{}`, then the `;` after `__set_error` will terminate both `if`-s and `else` will become orphaned - syntax error. I heard about the potential issue with mis-associated `else`, but I can't come up with a proper example. Maybe it is not really possible, just an urban legend. Anyone?
AndreyT
@AndreyT, thanks for correcting him. I wasn't entirely sure and didn't test it. But it makes sense. In fact, this is how the Standard formulates it: "In the second form of if statement (the one including else), if the first substatement is also an if statement then that inner if statement shall contain an else part.".
Johannes Schaub - litb
@Michael Speer: You are a moron. @AndreyT: Thank you for the correction. I have no idea why the hell that made sense to me when I typed it.
Michael Speer
+14  A: 

Here's your answer :

http://kernelnewbies.org/FAQ/DoWhile0

David V.
+1 for linking to reference.
Steven Sudit
+7  A: 

The purpose of do{ ... } while(0) construct is to turn a group of statements into a single compound statement that can be terminated with a ;. You see, in C language the do/while construct has one weird and unusual property: even though it "works" as a compound statement, it expects a ; at the end. No other compound constructs in C have this property.

Because of this property, you can use do/while to write multi-statement macros, which can be safely used as "ordinary" functions without worrying what's inside the macro, as in the following example

if (/* some condition */)
  __local_irq_save(x); /* <- we can safely put `;` here */
else
  /* whatever */;
AndreyT
+2  A: 

The answer has already been given (so the macro forces a ; when called), but another use of this kind of statement that I have seen: it allows break to be called anywhere in the "loop", early terminating if needed. Essentially a "goto" that your fellow programmers wouldn't murder you for.

do {
    int i = do_something();
    if(i == 0) { break; } // Skips the remainder of the logic
    do_something_else();
} while(0);

Note that this is still fairly confusing, so I don't encourage its use.

Toji
A: 

A statement is either { expression-list } or expression; so that poses a problem when defining macros that need more than one expression, because if you use { } then a syntax error will occur if the caller of the macro quite reasonably adds a ; before an else.

So the writer of the macro has to then either make it into a real function, wrap the construct in a single statement, or use a gnu extension.

The do .. while(0) pattern is the "wrap the construct" approach.

DigitalRoss