A nice little quickie: I briefly discuss in my assert ramblings why it’s important to wrap all of your multi-line macros in do/while(0) blocks. An unfortunate side-effect of this is that the construction

// NOISY CODE #define MULTI_LINE_MACRO \ do { \ std::printf("Hello "); \ std::printf("world!

"); \ } while (0)

will trigger C4127: “Conditional expression is constant” in Visual Studio 2003/2005, and probably 2008 as well (but I haven’t tried it). The culprit is the “while(0)” part, the compiler thinks we’re making a mistake when of course this is all very intentional. Let’s see what we can do to fix it!



This is really unfortunate because these macros live in header files, and for the life of me I couldn’t figure out a way to shut up that warning! The best way I’d found to deal with it was simply to issue a sledgehammer #pragma “shut the hell up” directive:

// QUIET BUT RUDE CODE #pragma warning(disable:4127) #define MULTI_LINE_MACRO \ do { \ std::printf("Hello "); \ std::printf("world!

"); \ } while (0)

This is very bad. I don’t want to globally silence 4127, just only for my multi-line macro stuff, but now every cpp file that #includes my header file has 4127 forever silenced! This is inconvenient enough inside your own codebase, but it’s flat-out rude if you’re writing middleware! I’ve used middleware that changes the warning levels on my cpp files, and it was an unpleasant surprise to find that the warnings I was expecting to receive were being muted before the compiler ever saw any of my code!

What we really want to do is something like this:

// BROKEN CODE #define MULTI_LINE_MACRO \ do { \ std::printf("Hello "); \ std::printf("world!

"); \ #pragma warning(push) #pragma warning(disable:4127) } while (0) #pragma warning(pop)

This preserves the global warning state, but still disables 4127 for the duration of our intentional violation. Unfortunately though, since the preprocessor isn’t recursive, that’s illegal. We can’t put #pragma directives inside macro definitions! We can also try doing something like this:

// STUPID CODE #pragma warning(push) #pragma warning(disable:4127) #define MULTI_LINE_MACRO \ do { \ std::printf("Hello "); \ std::printf("world!

"); \ } while (0) #pragma warning(pop)

But that doesn’t do what we want at all, because of course the #pragmas don’t wrap the macro at its invocation site, and wrapping the definition doesn’t do anything!

Well, it turns out that Microsoft implemented the __pragma directive for just this purpose. Using __pragma instead of #pragma lets you embed pragma directives inside of macros! This turns out to be just what the doctor ordered. We can now throw together multi-line macro wrappers that emit no warnings on /W4 and don’t change the global warning state!

// WORKING CODE #define MULTI_LINE_MACRO \ do { \ std::printf("Hello "); \ std::printf("world!

"); \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ } while (0) \ __pragma(warning(pop))

We can now generalize this approach to a set of helper macros that help you always correctly implement multi-line macros:

#define MULTI_LINE_MACRO_BEGIN do { #define MULTI_LINE_MACRO_END \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ } while(0) \ __pragma(warning(pop))

And here’s our original example reimplemented:

#define MULTI_LINE_MACRO \ MULTI_LINE_MACRO_BEGIN \ std::printf("Hello "); \ std::printf("world!

"); \ MULTI_LINE_MACRO_END

Finally, I should point out that both a coworker of mine and someone on the boost mailing list have found another slightly bizarre workaround to this problem: For some reason, the code

// WORKING CODE, BUT A LITTLE SCARY #define MULTI_LINE_MACRO \ do { \ std::printf("Hello "); \ std::printf("world!

"); \ } while (__LINE__ == -1)



doesn’t trigger C4127. I can only conclude that it’s unintended compiler behavior, because replacing __LINE__ with a literal integer causes it to trigger, and I can’t imagine that MS intended that code to be the correct way to dodge 4127. Use it at your own peril, and don’t be surprised if a new version of Visual Studio or some random service pack breaks it!

Filed under:

Stupid C++ Tricks by charles

