How to test if preprocessor symbol is #define'd but has no value?

前端 未结 9 1368
渐次进展
渐次进展 2020-11-30 06:08

Using C++ preprocessor directives, is it possible to test if a preprocessor symbol has been defined but has no value? Something like that:

#define MYVARIABLE         


        
相关标签:
9条回答
  • 2020-11-30 06:49

    You can use the BOOST_PP_IS_EMPTY macro like so:

    #include <boost/preprocessor/facilities/is_empty.hpp>
    
    #define MYVARIABLE
    #if !defined(MYVARIABLE) || !BOOST_PP_IS_EMPTY(MYVARIABLE)
        // ... blablabla ...
    #endif
    

    That did the trick for me. I shall add this macro is undocumented, so use it with caution.

    Source: preprocessor-missing-IS-EMPTY-documentation

    0 讨论(0)
  • 2020-11-30 06:51

    Mehrad's answer must be expanded to make it work. Also his comment

    /* MYVARI(A)BLE is undefined here */

    is not correct; to test for an undefined variable, there is the simple test #ifndef MYVARIABLE.

    After such test however, his expression leads to a correct solution of the original question. I tested that this code works, for undefined, defined but empty, and non-empty values of the macro MYVARIABLE:

    #ifndef MYVARIABLE
        /* MYVARIABLE is undefined here */
    #elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
        /* MYVARIABLE is defined with no value here */
    #else
        /* MYVARIABLE is defined here */
    #endif
    

    The #elif statement ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1 works as follows :

    • When MYVARIABLE is defined but empty, it expands to ~(~+0) == 0 && ~(~+1) == 1, which works out 0==0 && 1==1 (the double negation ~~ being an identity operator).
    • When MYVARIABLE is defined to a numeric value, say n, it expands to ~(~n+0)==0 && ~(~n+1)==1. On the left hand side of &&, the expression ~(~n+0)==0 evaluates to n==0. But with n==0, the right hand side evaluates to ~(~0+1)==1, with ~0 being -1 to ~(-1+1)==1, then ~0==1 and finally -1==1, which obviously is false.
    • When MYVARIABLE is defined to a non-numeric value, the precompiler reduces all unknown symbols to 0, and we get the previous case with n==0 once more.

    My complete test code (save as file test.c) :

    #include <stdio.h>
    
    int main() {
        printf("MYVARIABLE is "
    #ifndef MYVARIABLE
         "undefined"
    #elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
         "defined without a value"
    #else 
         "defined with this value : %i", MYVARIABLE
    #endif
        );
        printf("\n");
    }
    

    With the GNU preprocessor cpp you can experiment to see what code is produced:

    # undefined
    cpp test.c
    #defined without a value
    cpp -DMYVARIABLE= test.c
    #defined wit an implicit value 1
    cpp -DMYVARIABLE test.c
    #defined wit an explicit value 1
    cpp -DMYVARIABLE=1 test.c
    #defined wit an explicit value a
    cpp -DMYVARIABLE=a test.c
    

    or output of compilation and execution (under some linux)

    $ gcc -o test test.c ; ./test
    MYVARIABLE is undefined
    $ gcc -DMYVARIABLE= -o test test.c ; ./test
    MYVARIABLE is defined without a value
    $ gcc -DMYVARIABLE -o test test.c ; ./test
    MYVARIABLE is defined with this value : 1
    $ gcc -DMYVARIABLE=1 -o test test.c ; ./test
    MYVARIABLE is defined with this value : 1
    $ gcc -DMYVARIABLE=a -o test test.c ; ./test
    test.c: In function ‘main’:
    <command-line>:0:12: error: ‘a’ undeclared (first use in this function)
    ...
    

    In the last run, where MYVARIABLE is defined as 'a', the error is not an error in the macro definition; the macro is correctly lead to the last case, "defined with this value...". But this value being 'a', and 'a' not being defined in the code, the compiler or course has to signal this.

    In that way, the last case is a very good example of why the intent of the original question is very dangerous: via a macro the user can introduce any sequence of program lines in the code to be compiled. Checking that such code is not introduced, requires a lot more checking of the macro on valid values. Probably a full script is needed, instead of leaving this task to preprocessing. And in that case, what is the use of checking it in preprocessing too?

    0 讨论(0)
  • 2020-11-30 06:56

    I want to make sure that the project fails to compile if user forgot to define this string.

    While i'd check this in a previous build-step, you can do this at compile-time. Using Boost for brevity:

    #define A "a"
    #define B
    BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(A)) > 1); // succeeds
    BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(B)) > 1); // fails
    
    0 讨论(0)
  • 2020-11-30 06:58

    Soma macro magic:

    #define DO_EXPAND(VAL)  VAL ## 1
    #define EXPAND(VAL)     DO_EXPAND(VAL)
    
    #if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1)
    
    Only here if MYVARIABLE is not defined
    OR MYVARIABLE is the empty string
    
    #endif
    

    Note if you define MYVARIABLE on the command line the default value is 1

    g++ -DMYVARIABLE <file>
    

    Here the value of MYVARIABLE is 1

    g++ -DMYVARIABLE= <file>
    

    Here the value of MYVARIABLE is the empty string

    The quoting problem solved:

    #define DO_QUOTE(X)        #X
    #define QUOTE(X)           DO_QUOTE(X)
    
    #define MY_QUOTED_VAR      QUOTE(MYVARIABLE)
    
    std::string x = MY_QUOTED_VAR;
    std::string p = QUOTE(MYVARIABLE);
    
    0 讨论(0)
  • 2020-11-30 07:11

    I don't think that this can be done. That being said, I don't see a need for it. When you make a preprocessor #define symbol, you should establish a convention that either you define it as 1 or 0 for use in #if, or you leave it blank.

    0 讨论(0)
  • 2020-11-30 07:14

    For integer-only macros...

    You can use a hack with no extra macros:

    #if ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
    /* MYVARIBLE is undefined here */
    #endif
    
    0 讨论(0)
提交回复
热议问题