views:

179

answers:

4

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.

+2  A: 

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.

Reinderien
Yes it can be done ;-)
Martin York
+1  A: 

You can't since the preprocessor can only check for a numeric value. You string compare is not covered by preprocessor syntax.

harper
But any identifiers that are not defined evaluate to 0. So you can play little tricks with that fact
Martin York
+3  A: 

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
Georg Fritzsche
I think that is the answer. Making something fail on an undefined preprocessor macro sounds like an opposite of many questions here on SO.
Dummy00001
+6  A: 

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);
Martin York
Hm, that breaks with `#define A "a"`.
Georg Fritzsche
As the pre-processor can add quotes auto-magically but can not take them away. It is unusual to see quotes in macros. So it is quite easy to get around this limitation.
Martin York
But how would you handle `-DX="#"` or `-DY="a b"`?
Georg Fritzsche
This is quite clever, but it will break the build when `MYVARIABLE` is actually defined, since `EXPAND(MYVARIABLE)` will return non-integer value and compiler will stop at `#if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1)`
galets
@galets: Any identifier in a preprocessor condition is replaced as if it is a macro. If it is not defined then it returns 0. So the above code works fine as long as MYVARIABLE is EMPTY or MYVARIABLE is a valid identifier.
Martin York
@galets: Try: `#define MYVARIABLE Bob` for example works fine. EXPAND(MYVARIABLE) will return Bob1 which the preprocessor will replace with 0 as Bob1 is an undefined MACRO this will then fail the test 0 == 1 and thus the #if will not be expanded.
Martin York
@Martin York: I think I know what my problem is. The string I tested with ends with "!", so this code fails to compile: #define MYVARIABLE Test! #if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1) #endifwith: fatal error C1012: unmatched parenthesis : missing ')'
galets
@galets: As does anything with a space, `||`, `#`, ... in it.
Georg Fritzsche
Or more precisely if it contains anything other than "[A-Za-z_][A-Za-z0-9_]*" The above will fail
Martin York