I'd like a sequence of macros to replace the following code
#ifdef FOO
return true;
#else
return false;
#endif
with something like
return MAGICLY_EXPANDING_IFDEFINED_MACRO(FOO);
As you can guess, there are a lot of FOOs, enough that shrinking 4 lines to 1 would be cool. But really it would be replacing a monster switch statement with one line.
In C++, the behavior of defined
is specified only for conditional inclusion (#if
and #elif
). So you cannot use it in any other way.
(The relevant rules are found in section 16.1 of the Standard)
But, if you want to detect macros that specifically are #define
to the empty string, you don't need defined()
, you can do this:
#include <iostream>
/* this matches */
#define FOO
/* this will not */
#undef BAZ
/* nor will these */
#define BAR 0
//#define BAR 1
#define STRINGIZE(x) (#x)
#define EXPAND_IF_DEFINED(y) (!*STRINGIZE(y))
int main()
{
std::cout << +EXPAND_IF_DEFINED(FOO) << '\n'
<< +EXPAND_IF_DEFINED(BAR) << '\n'
<< +EXPAND_IF_DEFINED(BAZ) << '\n';
}
Warning, the -D
option will not be an empty string. To handle that case, you could use something like
#define IS_MACRO_DEFINED_OR_ONE(y) (!*STRINGIZE(y) || '1'==*STRINGIZE(y))
An even stronger test would be
#define IS_MACRO_DEFINED_NOT_TO_ITSELF(y) strcmp(#y, STRINGIZE(y))
Since literals are compile-time constants, the compiler will probably optimize that strcmp
call during compilation to a constant true
or false
. You could also make a constexpr
version of strcmp
to use.
For simple defines like #define FOO
or #define FOO 1
the below macros will work well, but much more universal solution is in @BenVoigt 's answer.
#define MAGICLY_EXPANDING_IFDEFINED_MACRO_I(X) (#X[0] == 0 || #X[0] == '1')
#define MAGICLY_EXPANDING_IFDEFINED_MACRO(X) MAGICLY_EXPANDING_IFDEFINED_MACRO_I(X)
DEMO
How it works?
That weird #
-something stringifies either the value of define, or the define name itself (if one is not defined). As such, for defined FOO
(with empty content) the compiler ends up with condition:
(""[0] == 0 || ""[0] == '1')
while for undefined FOO
the condition is:
("FOO"[0] == 0 || "FOO"[0] == '1')
That == 0
part matches the \0
character ending each raw string literal (even that empty one ""
), while the == '1'
condition is there in case someone defines value 1
, like in #define FOO 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With