Pavlo Muratov
Pavlo Muratov

Reputation: 317

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?

Upvotes: 5

Views: 1079

Answers (3)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275820

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 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 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.

Upvotes: 3

max66
max66

Reputation: 66230

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.

Upvotes: 4

Jarod42
Jarod42

Reputation: 218118

Use intermediate constrexpr variable:

constexpr auto value = ctcrc32("uPointLight.position")
uniformByNameCRC32(value);

Upvotes: 2

Related Questions