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:
using StrPtr = char const *;
StrPtr probe(StrPtr(MACRO1));
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:
char const *probe(char const *MACRO1);
... which is a function declaration where MACRO1
is the name of the parameter. But, when MACRO1
is defined to be "B"
, it becomes equivalent to:
char const *probe = (char const *) "B";
... 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:
if(!std::is_function<decltype(probe)>::value)
std::cout << "MACRO1 " << probe << '
';
We could make use of if constexpr
here, but std::cout
can output a function pointer (it converts it to bool
) 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:
#define PRINT_IF_DEFINED(z, n, data)
{
StrPtr probe(StrPtr(BOOST_PP_CAT(MACRO, n)));
if(!std::is_function<decltype(probe)>::value)
std::cout << "MACRO" BOOST_PP_STRINGIZE(n) " " << probe << '
';
}
#define PRINT_MACROS(num)
do {
using StrPtr = char const *;
BOOST_PP_REPEAT(num, PRINT_IF_DEFINED, ~)
} while(false)
... 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 :(