&. |

A software developer’s musings on software development

Macrolicious

Warning: I wrote this blog in 2008. That is a long time ago, especially on the internet. My opinions may have changed since then. Technological progress may have made this information completely obsolete. Proceed with caution.

I recently came across a clever way of writing preprocessor macros, and I figured that I would share.

Let’s say that for some reason you need to write a macro: MACRO(X,Y)1. You want this macro to emulate a function call in every way2.

Example 1: This should work as expected.

if (x > y)
  MACRO(x, y);
do_something();

Example 2: This should not result in a compiler error.

if (x > y)
  MACRO(x, y);
else
  MACRO(y - x, x - y);

Example 3: This should not compile.

do_something();
MACRO(x, y)
do_something();

The naïve way to write the macro is like this:

#define MACRO(X,Y)                       
cout << "1st arg is:" << (X) << endl;    
cout << "2nd arg is:" << (Y) << endl;    
cout << "Sum is:" << ((X)+(Y)) << endl;

This is a very bad solution which fails all three examples, and I shouldn’t need to explain why.

Now, the way I most often see macros written is to enclose them in curly braces, like this:

#define MACRO(X,Y)                         
{                                          
  cout << "1st arg is:" << (X) << endl;    
  cout << "2nd arg is:" << (Y) << endl;    
  cout << "Sum is:" << ((X)+(Y)) << endl;  
}

This solves example 1, because the macro is in one statement block. But example 2 is broken because we put a semicolon after the call to the macro. This makes the compiler think the semicolon is a statement by itself, which means the else statement doesn’t correspond to any if statement! And lastly, example 3 compiles OK, even though there is no semicolon, because a code block doesn’t need a semicolon.

The solution is kind of clever, I thought:

#define MACRO(X,Y)                         
do {                                       
  cout << "1st arg is:" << (X) << endl;    
  cout << "2nd arg is:" << (Y) << endl;    
  cout << "Sum is:" << ((X)+(Y)) << endl;  
} while (0)

Now you have a single block-level statement, which must be followed by a semicolon. This behaves as expected and desired in all three examples. I have noticed this macro pattern before, but I never really thought about why it was written this way. Mainly because I don’t often write macros to begin with.


  1. You should first ask yourself why you can’t just write a regular function and declare it inline, so that the compiler will do the work for you. I’m going to assume there is some good reason why you must use a macro. 

  2. Every way, that is, except that it can’t return a value. That gets much trickier and involves heavy abuse of the ?: operator, if it is even possible at all.