Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print macro values without knowing the amount of macros

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

like image 501
e271p314 Avatar asked Nov 23 '17 10:11

e271p314


People also ask

How do I print a macro value?

Here we will see how to define a macro called PRINT(x), and this will print whatever the value of x, passed as an argument. To solve this problem, we will use the stringize operator. Using this operator the x is converted into string, then by calling the printf() function internally, the value of x will be printed.

Can a macro return a value?

Macros just perform textual substitution. They can't return anything - they are not functions.

How do I know if a macro is defined?

Inside of a C source file, you can use the #ifdef macro to check if a macro is defined.


1 Answers

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 << '\n'; 

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 << '\n'; \     }  #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 :(

like image 194
Quentin Avatar answered Sep 25 '22 06:09

Quentin