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

Viewed 46831

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
#if !defined(MYVARIABLE) || #MYVARIABLE == ""
... blablabla ...
#endif

EDIT: The reason why I am doing it is because the project I'm working on is supposed to take a string from the environment through /DMYSTR=$(MYENVSTR), and this string might be empty. I want to make sure that the project fails to compile if user forgot to define this string.

10 Answers

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?

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

On a related note, often we want to check if a macro is undefined, is zero, or has an integer value other than zero:

#if ! defined(MYVARIABLE)
  // UNDEFINED:  MYVARIABLE is undefined
#elif ~ MYVARIABLE + 1
  // TRUE:       MYVARIABLE is defined and is empty or nonzero integer
#else
  // FALSE:      MYVARIABLE is defined and is zero
#endif

How does this work?

  • if MYVARIABLE is empty then ~ + 1 evaluates to nonzero (true)
  • if MYVARIABLE is nonzero (for example 1) then ~ 1 + 1 evaluates to nonzero (true)
  • if MYVARIABLE is zero then ~ 0 + 1 evaluates to zero (false)

This method works for any integer value for MYVARIABLE. When MYVARIABLE is the name of another macro, that macro's value will be used, as should be expected.

Note that if you just want to check if MYVARIABLE as a compile-time flag is enabled (defined and empty or nonzero):

#if defined(MYVARIABLE) && ~ MYVARIABLE + 1
  // the MYVARIABLE feature is enabled
#endif
Related