I’ve been tormented the past few days about an article I read several years ago. I barely even remember what it was about, only that the author played very clever tricks with the preprocessor’s token pasting functionality to push the limits of what the preprocessor can do. Maybe it was a parameterized repetition macro? Something like that. I do remember it took advantage of the asymmetry in expansion between function-like and object-like macros.
Anyways, I’ve been poking around the (very nice!) Chromium source code. One thing I took a close look at is how Google organized their logging macros. In the process, I noticed two tricks they used that I think are effectively preprocessor idioms, and since Google doesn’t turn up much for that search phrase, I figured I’d document what I saw.
The external interface of the Google logging system is pretty simple: the
LOG macro takes a severity parameter, such as
ERROR and expands into a temporary object that derives from
std::ostream. The tricks have to do with the severities: the tokens used as severities aren’t
#defined to the preprocessor! Instead, whatever is passed in is pasted onto a longer token that expands to an object declaration with the desired parameters. This lack of actual
#define statements effectively gives the severity tokens a specific local scope, even though the preprocessor deals only with explicitly global scope. And the one-to-many trick effectively forms a preprocessor-style switch() construct, where the macro parameter can affect the expansion of the rest of the macro.
Incidentally, now that I look, the Boost preprocessor library uses the same switch() trick to implement BOOST_PP_IF. Nifty.