Using constexpr to replace #define and #ifdef for conditional compilation

Viewed 7768

I am trying to replace the preprocessor #define and #if/#ifdef's that I am using to control conditional compilation with constexpr variables and ifs.

Is it possible to declare constexpr variables such that they reproduce the #defines in that they don't allocate runtime storage, and that taking the address of one leads to a compile time error?

Edited to add code example.

So in the headers I want to have something like

namespace ExampleNamespace
{
  enum class Platform : int {Darwin, Linux, Windows};

  constexpr Platform BuildPlatform = Platform::Darwin;  // Line A.
};

While in the code I want

if constexpr (Platform::Darwin == BuildPlatform)        // Line B.
  {
    cout << "Platform is Darwin" << endl;
  }
else
  {
    cout << "Platform is not Darwin" << endl;
  };

const Platform *const PlatformAddress = &BuildPlatform; // Line C.
const Platform &BuildPlatform2 = BuildPlatform;         // Line D.

Then aim is then to change the definition of BuildPlatform on Line A, such that Line B is evaluated at compile time (and the else clause gets discarded/not compiled) and lines C and D (and anything that does the same thing, or relies on runtime storage for BuildPlatform) generates a compiler error.

Is such a construct possible in C++17?

3 Answers

It is partially possible:

if constexpr (Platform::Darwin == BuildPlatform) {        // Line B.
    std::cout << "Platform is Darwin" << std::endl;
} else {
    std::cout << "Platform is not Darwin" << std::endl;
}

but as template <typename T> void foo() {static_assert(false);} is ill formed, code of all branches should have some kind of validity.

#ifdef (DarwinPlatform) // constexpr cannot be used here, you have to 
                        //rely on MACRO here
# include <darwin.h>     // Some OS specific header
#endif

void foo()
{
    if constexpr (Platform::Darwin == BuildPlatform) {
        DarwinMethod(); // Won't compile on other platforms as
                        // the method doesn't exist.
        // you should make the block template with template dependent code
        // to allow code to compile.
        // as http://coliru.stacked-crooked.com/a/c695575e4dcdecee
    }
}

Maybe you can use this.

enum class Platform { Darwin, Linux, Windows };

#ifdef __darwin__
constexpr Platform  BuildPlatform = Platform::Darwin;
#elif __linux__
constexpr Platform  BuildPlatform = Platform::Linux;
#elif __WIN32
constexpr Platform  BuildPlatform = Platform::Windows;
#endif

// your code then uses it like this

if constexpr (BuildPlatform == Platform::Darwin) 
{         
}
else if constexpr (BuildPlatform == Platform::Linux)
{
}
else if constexpr (BuildPlatform == Platform::Windows)
{   
}

For flags and integers, enum values work.

For floating point values, there is no constexpr way that guarantees no ODR usage. ODR usage tends to cause storage to be created for the constant.

You could use a constexpr function that returns a floating point value, but the function could easily take storage.

Related