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.
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.