Kip Macrolicious

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.

1
2
3
if (x > y)
  MACRO(x, y);
do_something();

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

1
2
3
4
if (x > y)
  MACRO(x, y);
else
  MACRO(y - x, x - y);

Example 3: This should not compile.

1
2
3
do_something();
MACRO(x, y)
do_something();

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

1
2
3
4
#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:

1
2
3
4
5
6
#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:

1
2
3
4
5
6
#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.
No Comments
RSS feeds: Kip's - Stephanie's - Both