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

前端 未结 9 1371
渐次进展
渐次进展 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: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 
    
    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’:
    :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?

提交回复
热议问题