Thu 10 May 2007
do { … } while (false);
Posted by Dennis B under Programming
[24] Comments
Let me share one of my favorite little programming idioms … do { … } while (false);
I picked it up from a friend who learned it from a friend, who probably saw it in a book. It may seem like a do-nothing, but it is a very nice low-budget and high performance exception handling mechanism. The typically application is something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 // setup
int status = 0;
do {
// preconditions
status = doSomething();
if (status) break;
status = doSomethingElse();
if (status) break;
// computation
status = doWhatYouWantedToInTheFirstPlace();
} while (false);
// cleanup
return status;
Without the DWF mechanism, the same code either requires either exceptions, duplicating cleanup code, or writing deeply nested if statements until all your preconditions are satisfied. Even though DWF initially may seem a little unorthodox, the code is actually very clear, readable, and maintainable.
24 Responses to “ do { … } while (false); ”
Comments:
Leave a Reply
Trackbacks & Pingbacks:
-
Pingback from xion.log » On Clever Usage of Python ‘with’ Clauses
January 21st, 2012 at 2:21 pm[...] is analogous to the break label mechanism from Java, while also serving similar purpose to infamous do-while(false) construct: with section() as s: for i in xrange(5): [...]
May 10th, 2007 at 1:04 pm
I do the same with a goto statement. Don’t freak out, you can use goto responsibly. You don’t need an extra variable, or a confusing do{…}while(false) statement. Everything unwinds nicely at the end of the function. It just works well, I probably wouldn’t use this do while thing at all. It’s functionally the same as the goto method and requires an extra variable, plus another control structure to interpret that variable.
May 10th, 2007 at 1:15 pm
You still need the status, unless you are going to put the calls in the if condition.
status = DoSomething();
if(!status)
{
status = DoSomethingElse();
}
if(!status)
{
status = DoWhatYouWantedToInTheFirstPlace();
}
May 10th, 2007 at 1:19 pm
Another approach that I use is to break this into two methods. something like this:
int m1() {
int status = 0;
// preconditions
status = doSomething();
if (status) return status;
status = doSomethingElse();
if (status) return status;
// computation
status = doWhatYouWantedToInTheFirstPlace();
return status;
}
void m2() {
int status= m1();
if (status) {
// cleanup
}
}
May 11th, 2007 at 6:18 am
Have to say I dislike this syntax. If the status code was some sort of error at any point, an exception should be thrown if the language allows.
If the language does not allow, or the status is not an error it would be far more appropriate to wrap the whole thing in a function and use return:
function doTheThing()
{
// preconditions
int status = doSomething();
if (status)
return status;
status = doSomethingElse();
if (status)
return status;
// computation
return doWhatYouWantedToInTheFirstPlace();
}
It makes the exit points from the function far more readable, and reduces nesting.
May 11th, 2007 at 6:31 am
Multiple exit points are a code smell. Code readability is more important, IMO, than heuristic performance enhancements.
May 11th, 2007 at 6:45 am
#5: why only have one exit point? How is it more readable to keep track of return code through a whole function having to nest a lot of code just so the only return point is the last line of the function?
IMHO the ‘flatter’ (i.e. less nested) a function is, the more readable it is. If the execution of line #3 of a 20 line function results in none of the rest of the function being executed, it is easier to read if it states that *explicitly* by using the ‘return’ keyword. It’s similar to the exception’s ‘fail-fast’ mechanism
My opinion only, but I feel strongly on this one…out of interest why do you believe multiple exit points make a function more readable?
May 11th, 2007 at 7:02 am
the code is actually very clear, readable, and maintainable. I disagree. You are using a loop as if it were a series of
statements. That’s clear to you? As anonymous said, multiple exit points are always a sign that you need to think twice.
statements are not your enemy, there is no reason to avoid them. If you are writing branching code, then avoiding them seems pathological to me. That’s why they exist!
May 11th, 2007 at 7:02 am
Another way of doing that would be like this:
boolean status = true;
status &= doSomething();
status &= doSomethingElse();
status &= doWhatYouWantedToInTheFirstPlace();
return status;
(Works for Java anyway, my memory of C logic operators is a little hazy)
May 11th, 2007 at 7:54 am
John I agree that the code is readable and maintainable, and I understand the reasoning behind the usage of the loop, but I disagree that multiple exit points are inherently bad – can you given a reason why?
If the condition on line X of a function being true prevents the remainder of the function from being executed and causes ’3′ to be returned, it should read like that:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
int status = 0;
if (condition1)
status = 1;
else if (condition2)
status = 2;
else
status = f2();
return status;
}
int g()
{
if (condition1)
return 1;
if (condition2)
return 2;
return f2();
}
The second function clearly shows that condition1 being true results in 1 being returned and no more code being executed.
To obtain the same insight into the first function you must read through the if..else.. structure, and check any code after it – simple in these contrived examples but in real code it can be harder.
Now, if we are in an environment where RAII cannot easily be applied – e.g. C – then continuing through the function until cleanup is necessary, but the second example shows far more easily what it is than the first.
May 11th, 2007 at 8:30 am
The reason that multiple exit points can be a problem goes back to the original assumptions: that there is cleanup code that needs to be executed before returning. Yes, you could cut and paste the cleanup code. I’ve seen plenty of code like that and plenty of bugs and memory leaks that stemmed from inconsistent cleanup paths.
May 11th, 2007 at 9:13 am
I would love to start using this idiom to replace code that looks like this:
http://go-oo.org/lxr/source/sc/sc/source/filter/xml/xmlimprt.cxx#2540
In the code base I now work on, there is a plenty of over-nested if-statement blocks like this one above. In fact, that one is not bad in comparison to some of the worse ones I’ve encountered.
Thanks for the good piece, Dennis!
May 11th, 2007 at 10:16 am
Welcome to “C” programming.
The standard macro is illustrated in http://en.wikipedia.org/wiki/C_preprocessor#Multiple_statements and widely used in well-written macros.
May 11th, 2007 at 12:55 pm
You could always use labeled breaks –
out: {
status = doSomething();
if (status) break out;
status = doSomethingElse();
if (status) break out;
status = doWhatYouWant();
}
May 11th, 2007 at 1:03 pm
Terrible, terrible, premature optimization.
Please… you really want to mess with stable and understood ideas such as exception handling? I’ve written this same code in C many many years ago, and I dont believe there’s a compelling reason to go back (even if it is to save a few cpu cycles).
May 11th, 2007 at 1:06 pm
Hah… saw this on dzone and just assumed it was java code. If this is C… then more power to ya.
Just dont be doing this in C# and Java! =)
May 13th, 2007 at 2:32 am
lovely tricks, and make code more cleaner instead of the horrible nested if, thanks for sharing
May 14th, 2007 at 11:25 am
I believe I first encountered the do{/*…*/}while(0) construct here:
http://c-faq.com/cpp/multistmt.html
May 14th, 2007 at 5:44 pm
Some of you are addressing an error handling arrangement (IMO misguided) as if it were a preprocessor construct that makes multi-line macros safe for all invocations(IMO very useful, although I don’t like multi-line macros unless I really have no choice due to the language). They are two very different applications of a construct that appears on the surface to be the same thing.
December 9th, 2007 at 3:25 am
if(dofirst() && dosecond() && dolast(){}
hmm
one line
no status variable
same result
December 9th, 2007 at 3:26 am
if(dofirst() && dosecond() && dolast(){}
January 20th, 2010 at 3:35 pm
This is substituting do-while for goto. Just use goto and be done with it.
February 21st, 2011 at 7:08 am
@neil
if(dofirst() && dosecond() && dolast(){ doverylast() }
February 21st, 2011 at 7:10 am
@neil
if(dofirst() && dosecond() && dolast()) { doverylast() }
(and we forgot a bracket there)