Compare the stringified macro (name) to the stringified (expanded) value of the macro:
#include <iostream>
#include <cstring>
#define TRACE_STRINGIFY(item) "" #item
#define TRACE(macro, message) \
do { \
if (strcmp("" #macro, TRACE_STRINGIFY(macro))) \
std::cout << message << "\n"; \
} while (0)
The "" # macro
expands to the macro name as a string, whereas TRACE_STRINGIFY(macro)
first expands the macro, then stringifies the result. If the two differ, macro
has to be a preprocessor macro.
This approach does fail for macros that are defined to themselves, i.e. #define FOO FOO
. Such macros are not detected as preprocessor macros.
Most compilers should be able to completely optimize away the comparison of two string literals. GNU GCC (g++) 4.8.2 definitely does even with -O0
(as does gcc for C -- the same approach obviously works in C, too).
This approach does work for function-like macros, but only if you retain the parentheses (and proper number of commas, if the macro takes multiple parameters) and it is not defined to itself (e.g. #define BAR(x) BAR(x)
).
For example:
#define TEST1 TEST1
#define TEST3
#define TEST4 0
#define TEST5 1
#define TEST6 "string"
#define TEST7 ""
#define TEST8 NULL
#define TEST9 TEST3
#define TEST10 TEST2
#define TEST11(x)
#define TEST13(x,y,z) (x, y, z)
int main(void)
{
TRACE(TEST1, "TEST1 is defined");
TRACE(TEST2, "TEST2 is defined");
TRACE(TEST3, "TEST3 is defined");
TRACE(TEST4, "TEST4 is defined");
TRACE(TEST5, "TEST5 is defined");
TRACE(TEST6, "TEST6 is defined");
TRACE(TEST7, "TEST7 is defined");
TRACE(TEST8, "TEST8 is defined");
TRACE(TEST9, "TEST9 is defined");
TRACE(TEST10, "TEST10 is defined");
TRACE(TEST11, "TEST11 is defined");
TRACE(TEST12, "TEST12 is defined");
TRACE(TEST13, "TEST13 is defined");
TRACE(TEST14, "TEST14 is defined");
TRACE(TEST1(), "TEST1() is defined");
TRACE(TEST2(), "TEST2() is defined");
TRACE(TEST3(), "TEST3() is defined");
TRACE(TEST4(), "TEST4() is defined");
TRACE(TEST5(), "TEST5() is defined");
TRACE(TEST6(), "TEST6() is defined");
TRACE(TEST7(), "TEST7() is defined");
TRACE(TEST8(), "TEST8() is defined");
TRACE(TEST9(), "TEST9() is defined");
TRACE(TEST10(), "TEST10() is defined");
TRACE(TEST11(), "TEST11() is defined");
TRACE(TEST12(), "TEST12() is defined");
TRACE(TEST13(,,), "TEST13(,,) is defined");
TRACE(TEST14(,,), "TEST14(,,) is defined");
return 0;
}
which outputs
TEST3 is defined
TEST4 is defined
TEST5 is defined
TEST6 is defined
TEST7 is defined
TEST8 is defined
TEST9 is defined
TEST10 is defined
TEST3() is defined
TEST4() is defined
TEST5() is defined
TEST6() is defined
TEST7() is defined
TEST8() is defined
TEST9() is defined
TEST10() is defined
TEST11() is defined
TEST13(,,) is defined
In other words, the TEST1
symbol is not recognized as defined (because it is defined to itself), nor are TEST11
or TEST13
without parentheses. These are the limitations of this approach.
Using the parenthesized form works for all parameterless macros (except TEST1
, ie. those defined to themselves), and all single-parameter macros. If a macro expects multiple parameters, you need to use the correct number of commas, as otherwise (say, if you tried TRACE(TEST13(), "...")
) you get a compile-time error: "macro TEST13
requires 3 arguments, only 1 given" or similar.
Questions?