I have code that includes a generated file (I don't know in advance its content), there is just a convention upon which me and my users agreed on how to create this file so I can use it. This file looks like
#define MACRO0 "A"
#define MACRO1 "B"
#define MACRO2 "C"
...
I want to print all macros values. My current code looks like
#ifdef MACRO0
std::cout << "MACRO0 " << MACRO0 << std::endl;
#endif
#ifdef MACRO1
std::cout << "MACRO1 " << MACRO1 << std::endl;
#endif
#ifdef MACRO2
std::cout << "MACRO2 " << MACRO2 << std::endl;
#endif
My question is, how to iterate over the macros in the generated file so I don't need to duplicate my code so much
First of all, we know we can count on Boost.Preprocessor for our looping needs. However, the generated code must work on its own. Unfortunately,
#ifdef
cannot work as a result of macro expansion, so there's no way to generate the code in your question. Are we toasted?Not yet! We can take advantage of the fact that your macros are all either nonexistent or a string literal. Consider the following:
We're taking advantage of our old friend the most vexing parse here. The second line can be interpreted in two ways depending on whether
MACRO1
is defined. Without it, it is equivalent to:... which is a function declaration where
MACRO1
is the name of the parameter. But, whenMACRO1
is defined to be"B"
, it becomes equivalent to:... which is a variable initialized to point at
"B"
. We can then switch on the type of what we just produced to see if a substitution occured:We could make use of
if constexpr
here, butstd::cout
can output a function pointer (it converts it tobool
) so the dead branch is valid, and the compiler is clever enough to completely optimize it out.Finally, we come back to Boost.Preprocessor to generate all that stuff for us:
... voilà!
See it live on Coliru
Note: the Coliru snippet includes warning disablers for GCC and Clang, which warn against our poor pal the most vexing parse :(
I ran into the same kind of need a long time ago.
My solution was to use the preprocessor, but not to get the answer "within the code".
For example,
clang++ -dM -E test.cpp
will output all the macros. (At the time, I usedgcc
, but the same technique works for GCC, CLang, and Visual Studio's CL.EXE ... the compiler switches may vary.)Ahh, drat, that also includes all the predefined macros.
So I would produce a "blacklist" file of the predefined macros that I did not care about, and then use that to filter out those results (using
grep -v
).The other problem I ran into was that sometimes someone would
#undef IMPORTANT_MACRO
which would then get missed in the dump. For those infrequent situations... and then the murders began.This answer is written taking a follow-up question into account.
C++ has support for generic programming that often eliminates need for preprocessor. In this case it would be better to make a set of type traits declaring properties of parameters that need to be handled reducing role of preprocessor to conditional compilation (or eliminating it completely if this code is supposed to be generated every time):
This will allow you to iterate over parameters and query their properties using generic code:
online compiler