Reputation: 19340
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.
Upvotes: 5
Views: 3010
Reputation: 48517
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)
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
Upvotes: 5
Reputation: 283803
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.
Upvotes: 6
Reputation: 6561
Macros are generally a bad route. What are you trying to accomplish? Something like this would work:
//Stash this away in a header file
#ifndef FOO
#define RETURN false
#else
#define RETURN true
#endif
return RETURN;
Upvotes: 2