Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why constexpr is not evaluated at compile time (MSVC 2015)?

recently I've tried to take advantage of C++0x constexpr under MSVC 2015 and my objective was to achieve compile-time hash strings. I wrote a simple FNV-1a hash algorithm as a constexpr function using, as required, a single return statement (ternary operator) and calling only constexpr functions, here it is:

template <size_t N>
constexpr U32 StringID_FNV1a_32(const char(&str)[N], I32 charIndex = 0, U32 hash = 2166136261U)
{
    return charIndex < N-1 ? StringID_FNV1a_32(str, charIndex +1, (hash ^ str[charIndex]) * 16777619U) : hash;
}

I also made a little macro to be able to change the algorithm under the hood without any trouble:

#define STRING_ID(str)  core::utility::StringID_FNV1a_32(str)

then I used this macro in my code, carefully checking if any breakpoint was hit and, also, the generated assembly code. Here's the little scenario:

//1. normal variable test
U32 hash1 = STRING_ID("abc");  

//2. enum test
enum {    
    hash2 = STRING_ID("abc")
};

//3. constexpr variable test
constexpr U32 hash3 = STRING_ID("abc");

And here the facts:

  1. first test was called at run time
  2. second test was performed at compile time
  3. third test was called at run time

As you can imagine I'm a little confused about the first and the third attempt.

Why in the third scenario is the compiler allowed to call the function at runtime? even though the msdn says clearly "The primary difference between const and constexpr variables is that the initialization of a const variable can be deferred until run time whereas a constexpr variable must be initialized at compile time." [https://msdn.microsoft.com/it-it/library/dn956974.aspx#Anchor_3]

Can be related to the fact that I'm in debug mode with all the optimizations turned off? and what about the first test?, is there any way to force the compiler to perform the hash at compile time?

like image 376
elven_inside Avatar asked Oct 31 '22 04:10

elven_inside


1 Answers

MSVC's behavior can be quite strange, however it is possible to force it to make constexpr functions run at compile time.

#define COMPILE_TIME(value) ((decltype(value))CompileTime<decltype(value), value>::ValueHolder::VALUE)

template<typename T, T Value>
struct CompileTime
{
    enum class ValueHolder : T
    {
        VALUE = Value
    };
};

This forces the value to be passed as a template argument + an enumeration value, thus making it strictly compile-time only.
Also please note that this works only for integer types.

You can use it simply by putting the call to the constexpr function as a parameter to the COMPILE_TIME macro:

constexpr U32 hash = COMPILE_TIME(STRING_ID("abc"));
like image 150
0x400921FB54442D18 Avatar answered Nov 16 '22 06:11

0x400921FB54442D18