Strange behaviour of #ifdef defined() in C++

145 Views Asked by At

I was writing cross platform code in Visual Studio Community, using C++20 and I am stuck on the output of following code:


#define WINDOWS (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
#define UNIX (defined(__unix__) || defined(__unix))

constexpr bool is_unix1()
{
#ifdef UNIX
    return true;
#else
    return false;
#endif
}

constexpr bool is_unix2()
{
#ifdef defined(UNIX)
    return true;
#else
    return false;
#endif
}


int main()
{
    cout << std::boolalpha << is_unix1() << " " << is_unix2() << endl;
}

When I ran this code in windows, from inside VS Community itself, I got the following output:

true false

Can someone explain why is_unix1() is evaluated as false while is_unix2() is evaluated as true?

I came to know that defined(...) is compiler specific, and it is not part of standard C++. But directly using the macro is resulting in strange behavior, and I am stuck on which approach to use here.

1

There are 1 best solutions below

2
On BEST ANSWER

defined is not compiler-specific. It is specified in the C++ standard.

#ifdef defined(UNIX) is simply not valid syntax and a compiler must diagnose this. #ifdef must be followed by a single identifier and then a new-line. It conditionally compiles code based on whether or not the identifier is defined as a macro.

#ifdef UNIX therefore always compiles the then-branch, because you defined UNIX as a macro beforehand.

What you want seems to be something like

#if (defined(__unix__) || defined(__unix))

#if conditionally compiles based on a given expression which can include defined operators, which individually evaluate to 0 or 1 depending on whether the identifier is defined as a macro.

However, you can't hide (defined(__unix__) || defined(__unix)) behind the UNIX macro and then have it expand in the controlling #if expression. If such an expansion results in the defined token, the behavior of the program is undefined.

So what you really want instead of your definition for UNIX is

#if (defined(__unix__) || defined(__unix))
#define UNIX
#endif

And then later

#ifdef UNIX