Checking if a preprocessor symbol is defined inside a macro

The usual way to check if a preprocessor symbol is set is #ifdef

. However, #ifdef

it cannot be used in a macro. I need a way to validate a macro if the argument to that macro is a specific preprocessor symbol.

For example:

#define TRACE(x,y) if(IS_DEFINED(x)){ std::cout << y; }

      

It TRACE

takes two arguments, the first x

must be the name of the preprocessor symbol. If such a symbol is defined, the second argument must be printed. The non-existent IS_DEFINED

function / macro is what I'm looking for.

Usage will be as follows:

#undef BLA
TRACE(BLA,"abc") // "abc" won't be printed, as BLA is not defined
#define BLA 1
TRACE(BLA,"xyz") // "xyz" will be printed, as BLA is a defined symbol

      

Is there a way to achieve this? Maybe some kind of macromagic? Of course thes solution should work for any character, not just BLA

or a hard character set. Obviously, it is quite easy if the set of characters to be checked is known in advance.

+3


source to share


5 answers


Sample code:

#include <iostream>

#define TRACE(name, msg) TRACE_EVAL_(TRACE_DO_, name, msg)
#define TRACE_EVAL_(macro, ...) macro(__VA_ARGS__)
#define TRACE_DO_(name, msg) \
    (#name[0] == 0 || #name[0] == '1' ? (void)(std::cout << (msg)) : (void)0)

#undef  FOO
#define BAR
#define BAZ 1

int main() {
    TRACE(FOO, "foo\n");
    TRACE(BAR, "bar\n");
    TRACE(BAZ, "baz\n");
}

      



Adjust the test TRACE_DO_

according to the possible values ​​of the macro definition. Note that supporting definitions for non-numeric values ​​can be problematic, as it would be difficult to distinguish them from macro names ...

+4


source


Compare the string macro (name) with the string (extended) 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)

      

"" # macro

expands to a macro name as a string, whereas TRACE_STRINGIFY(macro)

expands the macro first and then builds the result. If they are different from each other, there macro

must be a preprocessor macro.

This approach is not suitable for macros that are defined by themselves, i.e. #define FOO FOO

... Such macros are not recognized as preprocessor macros.

Most compilers should be able to fully optimize 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 as well).

This approach works for macros like functions, but only if you keep the parentheses (and the correct number of commas if the macro takes multiple parameters) and it is not self-defined (for example #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 character is TEST1

not recognized as being defined (since it is defined on its own) and also TEST11

or TEST13

without parentheses. These are the limitations of this approach.

The use of the round form works for all macros without parameters (except TEST1

, i.e., defined for oneself) and all one-parameter macros. If the macro expects multiple parameters, you need to use the correct number of commas, otherwise (say, if you tried TRACE(TEST13(), "...")

), you will get a compile-time error: "macro TEST13

requires 3 arguments, only 1 is specified" or similar.

Questions?

+3


source


If you can make BLA

always equal to 0

or 1

(or some other value converted to bool

), you can do

#define TRACE(x, y) if (x) { std::cout << y << std::endl; }

      

A decent compiler will optimize the constant expression in if

, so this method comes with no extra cost.

Update: Code for optionally defined macros:

#define IS_DEFINED(x) IS_DEFINED2(x)
#define IS_DEFINED2(x) (#x[0] == 0 || (#x[0] >= '1' && #x[0] <= '9'))
#define TRACE(x, y) if (IS_DEFINED(x)) { std::cout << y << std::endl; }

      

Demo

Note that it only works if FOO

undefined or undefined for empty or specific for some number.

How it works
For functionally similar macros, extension works as follows: first all macro arguments are expanded, if they are not used with #

or ##

, then the macro itself is expanded (see the formal explanation in the chapter on 16.3.1

the C ++ standard, or 6.10.3.1

the C standard).

So IS_DEFINED(x)

"calls" IS_DEFINED2(x)

with an extended macro x

. If x

defined, it will be replaced with whatever it is defined for, otherwise it will be passed as is.

If you call IS_DEFINED2(FOO)

directly, it #x

will always be equal "FOO"

, which doesn't suit you.

+1


source


An alternate version, based on Christoph's answer, is the one I made to allow the variadic parameters to be passed to the logging method (which is Objective-C in this case, but should work in C ++ as well).

#define TGLog(tag, msg, ...) TGLog_eval_(TGLog_do_, tag, msg, ## __VA_ARGS__)
#define TGLog_eval_(macro, ...) macro(__VA_ARGS__)
#define TGLog_do_(tag, msg, ...) \
(#tag[0] == 0 || #tag[0] == '1') ? NSLog(@"%s",[NSString stringWithFormat:msg, ## __VA_ARGS__]) : (void)0;

      

The ## appended before the variable argument variable removes the preceding comma if there are no arguments at all. This ensures that the expansion does not end with a trailing comma.

+1


source


Linux ' kgconfig.h

defines a macro __is_defined

for this use case:

 #define __ARG_PLACEHOLDER_1 0,
 #define __take_second_arg(__ignored, val, ...) val

/*
 * Helper macros to use CONFIG_ options in C/CPP expressions. Note that
 * these only work with boolean and tristate options.
 */

/*
 * Getting something that works in C and CPP for an arg that may or may
 * not be defined is tricky.  Here, if we have "#define CONFIG_BOOGER 1"
 * we match on the placeholder define, insert the "0," for arg1 and generate
 * the triplet (0, 1, 0).  Then the last step cherry picks the 2nd arg (a one).
 * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
 * the last step cherry picks the 2nd arg, we get a zero.
 */
#define __is_defined(x)         ___is_defined(x)
#define ___is_defined(val)      ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

      

It's C99 and works for tristate options (undefined defined as 0 defined as 1).

0


source







All Articles