How to check if constexpr is calculated correctly

I used constexpr to compute hash codes at compile time. The code compiles correctly, runs correctly. But I do not know if the hash values ​​are compilation time or runtime. If I spend code at runtime, I do not execute constexpr functions. But they are not even tracked for runtime values ​​(calculate the hash for the generated runtime string are the same methods). I tried to make out the disassembly, but I do not understand it at all.

For debugging purposes, my hash code is just a string length using this:

constexpr inline size_t StringLengthCExpr(const char * const str) noexcept { return (*str == 0) ? 0 : StringLengthCExpr(str + 1) + 1; }; 

I have an ID class created this way

 class StringID { public: constexpr StringID(const char * key); private: const unsigned int hashID; } constexpr inline StringID::StringID(const char * key) : hashID(StringLengthCExpr(key)) { } 

If I do this in the main method program

 StringID id("hello world"); 

I got this parsed code (part of it - basically there are many other methods and other things)

 ;;; StringID id("hello world"); lea eax, DWORD PTR [-76+ebp] lea edx, DWORD PTR [id.14876.0] mov edi, eax mov esi, edx mov ecx, 4 mov eax, ecx shr ecx, 2 rep movsd mov ecx, eax and ecx, 3 rep movsb // another code 

How can I say from this that the "hash value" is compilation time. I do not see a constant like 11 to register. I am not very good at ASM, so maybe that’s right, but I’m not sure what to check or how to be sure that the values ​​of the “hash code” are compilation time and are not calculated at runtime from this code.

(I am using Visual Studio 2013 + Intel C ++ 15 Compiler - VS Compiler does not support constexpr)

Edit:

If I change my code and do it

  const int ix = StringLengthCExpr("hello world"); mov DWORD PTR [-24+ebp], 11 ;55.15 

I have the correct result

Even so

change personal hashID to public

  StringID id("hello world"); // mov DWORD PTR [-24+ebp], 11 ;55.15 printf("%i", id.hashID); // some other ASM code 

But if I use private hashID and add getter

  inline uint32 GetHashID() const { return this->hashID; }; 

for class id then i got

  StringID id("hello world"); //see original "wrong" ASM code printf("%i", id.GetHashID()); // some other ASM code 
+6
source share
4 answers

The most convenient way is to use constexpr in the static_assert . The code will not compile when it is not evaluated at compile time, and the static_assert expression static_assert not give you any overhead at runtime (and without unnecessary generated code, as is the case with the template solution).

Example:

 static_assert(_StringLength("meow") == 4, "The length should be 4!"); 

It also checks if your function is computed correctly or not.

+17
source

If you want to make sure that the constexpr function is evaluated at compile time, use its result for something that requires a compile time estimate:

 template <size_t N> struct ForceCompileTimeEvaluation { static constexpr size_t value = N; }; constexpr inline StringID::StringID(const char * key) : hashID(ForceCompileTimeEvaluation<StringLength(key)>::value) {} 

Note that I renamed the function only StringLength . A name starting with an underscore followed by a capital letter or containing two consecutive underscores is not legal in the user code. They are reserved for implementation (compiler and standard library).

+4
source

There are several ways to force compile time estimates. But it is not as flexible and easy to configure as you expected when using constexpr . And they won’t help you find out if compile-time constants were actually used.

What would you like for constexpr to work where you expect its usefulness. Therefore, you are trying to satisfy his requirements. But then you need to check if the code that you expect to receive at compile time has been generated, and if users actually consume the generated result or run the function at runtime.


I found two ways to determine if a class or (member) function uses a path to evaluate compilation time or runtime.

  • Using the constexpr property of functions that return true from the noexcept ( bool noexcept( expression ) ) bool noexcept( expression ) if it is evaluated at compile time. Since the generated result will be a compile time constant. This method is quite accessible and applicable using Unit-testing.
    (Keep in mind that labeling these functions clearly noexcept will break the test.)

Source: cppreference.com (2017/3/3)
Since the noexcept operator always returns true for a constant expression, it can be used to check whether a particular call to the constexpr function accepts a constant branch of the expression (...)

  1. (less convenient) Using a debugger: By placing a breakpoint inside a function labeled constexpr . Whenever the breakpoint does not start, the result of the compiler evaluation was used. Not the easiest, but possible random check.

Sura: Microsoft Documentation (2017/3/3)
Note. In the Visual Studio debugger, you can determine whether the constexpr function will be evaluated at compile time by placing a breakpoint in it. If a breakpoint is hit, the function is called at run time. If not, the function is called at compile time.

I found both of these methods useful when experimenting with constexpr . Although I have not tested the environment outside of VS2017. And it was not possible to find an explicit statement supporting this behavior in the current draft of the standard.

0
source

The following trick can help check if the constexpr function was evaluated only at compile time:

Using gcc, you can compile the source file listing the assembly + with sources; both constexpr and its calls are in the source file try.cpp

  gcc -std=c++11 -O2 -Wa,-a,-ad try.cpp | c++filt >try.lst 

If the constexpr function was evaluated at runtime, then you will see the compiled function and the call instruction (call function_name on x86) in the try.lst assembly list (note that the C ++ filter command is not equipped with the name builder)

Interestingly, I always see a call if it is compiled without optimization (without the -O2 or -O3 option).

0
source

Source: https://habr.com/ru/post/983651/


All Articles