Fun With do {} while(FALSE);

[Note: This was originally written for "FYI", the internal newsletter for the worldwide software engineering departments of Dendrite International]

Due to the overwhelming demand (i.e., Rob asked me), it has come time for me to share the cute do {} while(FALSE) trick I mentioned in a past issue. Consider how you would attempt to write a macro that called two functions. For example, you might try something like this:

#define SetSize(x,h,w)	SetHeight(x,h);\
			SetWidth(x,w) 

However, if you tried using that in an if() statement, such as:if (x !=0) SetSize(x,h,w);

it would expand to:

if (x !=0)
	SetHeight(x,h);

SetWidth(x,w);

with the second half outside the if() statement — not at all what you'd want. OK, strike one -- Next pitch, we try wrapping the two lines in the #define in curly braces:

#define SetSize(x,h,w)	{	SetHeight(x,h);\
				SetWidth(x,w); }

Now, this time when we expand the if() it becomes:

if (x !=0)
{
	SetHeight(x,h);
	SetWidth(x,w);
}
;

Now everythings cool, right? But--what about that semi-colon at the end? It doesn't belong there, now does it? But, since it evaluates to just a null statement, it won't hurt anything-- Right? Think so? Well, consider the following:

if (h !=0)
	SetSize(x,h,w);
else
	SetSize(x,h1, w1);

Now when we expand that, it becomes :

if (h !=0)
{
	SetHeight(x,h);
	SetWidth(x,w);
}
;
else
{
	SetHeight(x,h1);
	SetWidth(x,w1);
}
;

and we have the pesky semi-colon/null statement in the middle -- which will cause compiler errors, since the else doesn't immediately follow an if. Strike Two. OK, now to get around that problem, we have to remember when using the macro, to leave off the final semi colon:

if (x !=0)
	SetSize(x,h,w)
else
	SetSize(x,h1,w1)

But, this is easily forgotten, and very confusing for others who will have to eventually maintain your code, or even use that macro in their own code. Even if the restriction were well documented, it still requires the user of the function to know far too much about the implementation of the function. The function should be a black box — Other programmers have their own problems to worry about, and shouldn't have to think about your problems as well.

Which brings us to do {} while (FALSE):. Now, if we were to defined the original macro as:

#define SetSize(x,h,w)	do { \
			SetHeight(x,h);\
			SetWidth(x,w); \
			} while (FALSE)

our if()...else will now expand to:

if (x !=0)
	do {
		SetHeight(x,h);
		SetWidth(x,w);
	   } while(FALSE);
else
	do { ....

which is a completely proper syntax. Now, consider what it's saying: Do these statements once, then test to see if we should do them again. The test is FALSE, so we stop there. Of course, the optimizer will detect the constant in the comparison, and remove the test, so we're left just the two calls we wanted in the first place. So, the fact that our function SetSize() is in reality a macro is completely hidden from those future programmers who will be using it, since it performs exactly like a function.

Oh, by the way, if we were using C++, we could have just written the original macro as:

inline void SetSize(int x,int h,int w)
{
	SetHeight(x,h);
	SetWidth(x,w); 
}

and have been done with it. This would have accomplished the same effect, of making the two calls without the additional overhead of the extra function call, plus added type checking and avoided the side effects possibly caused by using "x" twice......

Copyright © 1994, James M. Curran