Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using return value of constexpr function as parameter to another function

I have a constexpr function that computes CRC32 hash from string literal.

template <size_t len>
constexpr uint32_t ctcrc32(const char (&str)[len]) {
    return detail::crc32<len - 2>(str) ^ 0xFFFFFFFF;
}

(it refers to other constexpr functions)

What I want to do is to call some other function that accepts uint32_t value and uses it to access data in some unordered_map. Such call looks like this:

uniformByNameCRC32(ctcrc32("uPointLight.position"));

I expect that "uPointLight.position"'s hash computes once at build time and then a resulting constant is passed to uniformByNameCRC32(), but that is not the case and ctcrc32() is called at runtime which kills CPU basically, since I have a lot of uniformByNameCRC32() calls.

This, however, works fine:

std::array<uint64_t, ctcrc32("string_literal")> array;

Such code compiles and indicates that ctcrc32()'s return value is indeed a constexpr.

What am I missing here?

like image 790
Pavlo Muratov Avatar asked Oct 30 '17 17:10

Pavlo Muratov


People also ask

Can a function return constexpr?

A constexpr function is a function that can be invoked within a constant expression. A constexpr function must satisfy the following conditions: It is not virtual. Its return type is a literal type.

Can a function parameter be constexpr?

We allow annotating a function parameter with constexpr with the same meaning as a variable declaration: must be initialized with a constant expression.

When to use #define vs constexpr?

#define (also called a 'macro') is simply a text substitution that happens during preprocessor phase, before the actual compiler. And it is obviously not typed. constexpr on the other hand, happens during actual parsing. And it is indeed typed.

Can you modify constexpr?

A const int var can be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed. A constexpr int var cannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.


3 Answers

The OP ask (in a comment)

how to wrap it in some macro since I don't want to write two lines of code each time

I suppose you can use a function that receive the ctcrc32 value as template value and simply return it.

I mean

template <uint32_t N>
constexpr uint32_t getCV () // get constexpr value
 { return N; }

that you can use as follows

uniformByNameCRC32(getCV<ctcrc32("uPointLight.position")>());

Passing the ctcrc32() value to getCV() as template parameter force the compiler to calculate it compile time.

like image 186
max66 Avatar answered Oct 19 '22 04:10

max66


There are no guarantees anything is done at compile time vs run time in C++. C++ in theory permits your code to be passed as a string literal to a C++ interpreter at runtime. (There are some mandatory diagnostics for some ill-formed programs, but the form of said diagnostics is not specified, and you are permitted to emit a diagnostic even if there is no ill-formedness, so simply printing out a line saying "this code will be compiled later" satisified the standard).

constexpr simply lets you do some things in code that traditionally are done at compile time, like size arrays on the stack or use constants in the name of a type.

There are zero major C++ compilers which name new types at runtime. So, we have an in to force something to run at compile time; in c++14 we have template constants:

template<uint32_t v>
std::integral_constant< uint32_t, v > kint32{};

With that, we can do:

uniformByNameCRC32(kint32<ctcrc32("uPointLight.position")>);

should execute ctcrc32 at compile time. Not doing so would require a lot of work by the compiler.

In c++17 you can even do:

template<auto x>
std::integral_constant< std::decay_t<decltype(x)>, x > k{};

and it works for any type.

std::integral_constant in turn implicitly converts back to a value of the same type.

like image 31
Yakk - Adam Nevraumont Avatar answered Oct 19 '22 06:10

Yakk - Adam Nevraumont


Use intermediate constrexpr variable:

constexpr auto value = ctcrc32("uPointLight.position")
uniformByNameCRC32(value);
like image 43
Jarod42 Avatar answered Oct 19 '22 05:10

Jarod42