Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macros to return defined(X) as true or false

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.

like image 286
Andrew Lazarus Avatar asked Oct 09 '14 19:10

Andrew Lazarus


2 Answers

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.

like image 168
Ben Voigt Avatar answered Oct 06 '22 08:10

Ben Voigt


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


Q&A section

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

like image 23
Piotr Skotnicki Avatar answered Oct 06 '22 09:10

Piotr Skotnicki