Trivial Do While Loop

A trivial do/while loop is a useful device in CeeLanguage consisting of a do/while loop with a loop condition which always fails; i.e:

 do {
	/* Execute some code */
 } while (0); 

It's also useful (but to a lesser extent) in CeePlusPlus and JavaLanguage, though the presence of other language features such as destructors (C++), finally (Java), exceptions (both), and inline functions (C++) eliminates the need in some cases.

This is useful in two instances.

 #define FOO(x) { do_this(x); do_that(); }
doesn't work in the following context

 if (x>y)
	FOO(x);
 else
	FOO(y);
The above won't compile. By replacing the macro definition with the following (note the absence of a trailing semicolon)

 #define FOO(x) do {do_this(x); do_that(); } while (0)

the above if/else clause works properly. This technique is sometimes known as swallowing the semicolon.

Advocates of HighLevelLanguages? (higher than C, which seems to be any modern language in existence, except for assembly) will of course object that such bletcherous hackery is entirely unnecessary in their favorite language. And they'd be right; however lots of us still write code in C; this trick is useful to know.

-- ScottJohnson

This is also a good demonstration of one (of many) reason that C gave 'macros' a bad name

Disallow the use of ; as a null statement; require {} instead. --AdamBerger

This problem isn't only in C The same semi-colon problem is in Bourne shell and Bash (yes its not a compiled language) also do not allow just a lonely semicolon in if statements.

  if program; then
    ;
  else
    echo some error

The solution here is to use ':;' instead of just ';' The ':' is a 'no-op' shell command, always true.

--Anthony Thyssen

I never considered anything like that in my years programming C. A forward goto is a lot simpler, as long as you don't follow GotoConsideredHarmful religiously. The second concern didn't apply, since I rarely used macros and always put braces around if, else, for, and while clauses.

You never wrote a header file that someone else would ever include??? Because the above point is critically important for actually published macros, else you'll break someone else's code.

[Discussion of C preprocessor moved to CeePreprocessorStatements]


I have used a similar-but-different construct in building initialization code sequences, using the switch(){} statement, thus (crudely):
	switch (1) { default:
	.
	.
	(pass/fail operations)
	.
	.
	}
although, some compilers insist on at least one case, so for these:
	switch (1) { case 0: default:
	.
	.
	(pass/fail operations)
	.
	.
	}
As mentioned above, in the first example, I don't return from such a construct, as this violates single-entry/single-exit structure. -- GarryHamilton

Surely not for the same purpose, though, since this runs afoul of the semicolon problem... so what are you saying you use it for?

Quite right, I don't use this for macros. My first use was in writing an initialization sequence for a dial-in driver for a terminal emulator running on a PC to establish secure access to a SeriesOne? (IBM box). The problem was that NestedIfs? became so unwieldy and ugly that I sought to accomplish an "everything succeeds or fall to the bottom with fail code" structure. I tried both the TrivialDoWhileLoop and the TrivialSwitch?, and stayed with the switch.

The sequence involved something like this: if I have a comm port and it's really there and it's connected to a modem of a type I recognize and it responds to the setup sequences as I expect and it is connected to a phone line and detects a dial tone and will dial out to the remote modem and the remote modem answers and I get connected to the remote SeriesOne? and I receive the challenge and I'm able to send the response and I get the go-ahead from the Series 1 driver, then succeed and return to caller with open channel. If at any point any of these steps fails, then fail and return error code to caller.

The switch structure allowed the code to be arranged vertically rather than successively indented. If any return code came back non-zero, a simple "break" dropped past the rest of the code to the bottom of the switch.

This use is in line with the first bullet point at the top of the page, above the #define.

That's interesting. Long ago the do-while(0) worked for me for guarded conditions so I never really reconsidered. I'll have to think about that.


A TrivialDoWhileLoop is IMHO a CodeSmell asking for ExtractMethod (replacing the breaks with returns).

If only it were so easy to to follow such simple sounding advice, however you missed the explanation at the top of the page, that this is essentially required for certain types of macros. There is no escape from it, in that context.

I've never needed it for my own macros. Maybe it's because of my coding conventions. Come to think of it, I don't think I've written any C macros that got used by other people.

[ I fail to see how you can write macros safely without following the advice at the top of the page, coding conventions or not, other people or not. That's the point of the macro-related part of this page. Oh, or do you mean you don't put statements in macros? That'll do it. ]

As for the other use discussed above, the one where "switch" was also offered as an alternate approach (but I assume you would still disapprove), sometimes you could do ExtractMethod, sometimes you can't. Sometimes one is doing guards, a long series of them, and if any fail, you don't want to do the final block of code. You could use a 20 line boolean expression, but that actually is much uglier, which is why do-while(0) and switch() even arise. Why couldn't you do ExtractMethod there? What if each clause of the series of guards used a different local variable? Then you'd need to pass all twenty local variables to the new method.

I usually find that guard clauses use member variables, so I haven't had the problem. Then again, the original context was CeeLanguage so I see your point.

Sometimes the purported cure is worse than the disease.

Point well taken, generally speaking at least.


(moved from AlwaysUseBracesOnIfThen -- EditHint: squeeze out redundancy)

use the TrivialDoWhileLoop in your macros to guard against people who fail to AlwaysUseBracesOnIfThen when they use your macros

Another probable error that is likely to pop up if you don't use braces, is if the one statement after the if is a macro. I once turned a macro containing a simple function call into one using two statements, and my code broke half a mile further down the road. Had braces been used by other programmer on the if statement, or by me on the #define statement, then I wouldn't have spent several hours trying to trace down why my program was inexplicably exiting.... So this entry should actually be named AlwaysUseBraces?. -- AndreSlabber

The trick here, if I recall, is to usually enclose macro definitions in (). If you have multiple statements, separate them with commas, ie:

  #define macro() (foo(), bar())
If you also have control structures if your macro, then wrap the whole in a do...while statement, like this:

  #define macro(x, y)	do {	if (something_is_true) {	 x;	} else {	 y;	}	} while (0)
''Actually, it also works if you just use curly braces and semicolons like this:

	#define macro	{statement1; statement2 }
-- AndreSlabber''

Actually not, because the above (macro statements enclosed in curlies) has the problem of becoming two statements where the macro name is used followed by a semicolon. That's what the while (0) method is solving: keeping it one statement. Otherwise, with the curlies, we get:
  if (a)
	macro();	// expands to:  {statement1; statement2;};
  else
	something();
Sure, it doesn't compile - so at least it isn't a sneaky error - but in any case the while (0) approach lets it work properly. -- BillKelly

Still, the onus is on the writer of the macro to make it safe by using braces or parens, etc. The idea of braces on the if-then is different, since the modifying is done the the straight code, just inobservant of the blocking. -- Pete Hardie

Of course, I'm not saying that I shouldn't have correctly written the macro; I just pointed out that ifs without braces create a nasty little error if you don't think of that. Actually, I think coding straight into the if statement and then doing it wrong is worse, because one can clearly see that there are no braces, and that they should be inserted. -- AndreSlabber



Merged from orphan DisguisedGoto'

A programmer I work with often uses the following construct:

 do {
    ...
    if ( something ) break;
    ...
 } while (0);

I could not understand why he insisted on writing his code that way... until a friend pointed out to me that this was a GOTO in disguise. The code is similar (though not identical to):

  { /* provide a nested scope */
     ...
     if (something) goto done;
     ...

done: ; /* I think the semicolon following the label is necessary; it * certainly is in a switch statement */ }

A similar (and real), but more mysterious version:

 for(;;){
    ...
    if(something)break;
 }

...which strikes as a do-while non-believer.

Well, I'd say the latter is less mysterious. An infinite loop with a break is easy to grasp. The while(0) stumped me when I first saw it. Why on earth write a loop that will execute exactly one time?? (The answer: to conceal a GotoStatement?.)

Why should anybody use it, instead of:

 ...
 if (!something) {
   ...
 }

There's another reason for do ... while(0) construct: preprocessor macros. It's not something I've used, but on page 174 of CodeReading, the author discusses a use of do .. while(0) to create a block in a macro. He gives an example from the NetBSD vm source.
The do ... while(0); construct is used in macros like this:

#define M do { stmt; stmt; } while(0)

   if (x) M;
   else 
      ...

Other ways of having multiple statements in a macro do not allow you to freely add or leave off a ; after invoking the macro. The do while(0) packages up the multiple statements syntactically into a single statement, which can be used in any context.

Had the macro been:

 #define  M   stmt; stmt; 

then the following does not work:

 if (x) M    // fails, second stmt; not controlled by if

Or had it been:

 #define M  {stmt; stmt; }

then the following does not work:

 if (x) 
    M;     // ; not permitted after } before else
 else
    stmt; 

The do { } while(0) works in all the cases.

The above M macro is a bad coding style in my books, regardless of while(0). In C, macros should be restricted to defining constants or trivial one-liners like max. If you need to tack multiple statements together, put them in a function. In C++, there is little excuse to use macros at all.


Jumping out of a block is not always bad. It should be used judiciously, but sometimes it simplifies the code.


Another use for the TrivialDoWhileLoop, not mentioned above, is to stop a function being unexpectedly executed multiple time in a macro pretending to be or substituting a function, for example:

#define pretend_function(x) do { int tmpx = (x); if( 0 < tmpx && tmpx < 15 ) { do_something(); } } while(0)

So, if some coder does:

pretend_function( other_function() );

...then other_function is only executed once. Obviously, if we were to have done:

#define incorrect_function(x) do { if( 0 < (x) && (x) < 15 ) { do_something(); } } while(0)

...then the code for other_function() is executed twice, which may have unexpected results or, at best, waste cycles in the case of a pure function.

-- C 2005-12-11


CeeIdioms

EditText of this page (last edited May 30, 2012) or FindPage with title or text search