How to Test If a Constexpr Function Is Evaluated at Compile Time

Is it possible to test if a constexpr function is evaluated at compile time?

The technique listed works, but since it uses static_assert it is not sfinae friendly. A better way (in theory, you'll see what I mean) to do this is to check whether a function is noexcept. Why? Because, constant expressions are always noexcept, even if the functions are not marked as such. So, consider the following code:

template <class T>
constexpr void test_helper(T&&) {}

#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))

test_helper is constexpr, so it will be a constant expression as long as its argument is. If it's a constant expression, it will be noexcept, but otherwise it won't be (since it isn't marked as such).

So now let's define this:

double bar(double x) { return x; }

constexpr double foo(double x, bool b) {
if (b) return x;
else return bar(x);
}

foo is only noexcept if the x is a constant expression, and b is true; if the boolean is false then we call a non constexpr function, ruining our constexpr-ness. So, let's test this:

double d = 0.0;

constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));

std::cerr << x << y << z;

It compiles, great! This gives us compile time booleans (not compile failures), which can be used for sfinae, for example.

The catch? Well, clang has a multi-year bug, and doesn't handle this correctly. gcc however, does. Live example: http://coliru.stacked-crooked.com/a/e7b037932c358149. It prints "100", as it should.

When does a constexpr function get evaluated at compile time?

constexpr functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well. A constant expression could be a literal (like 42), a non-type template argument (like N in template<class T, size_t N> class array;), an enum element declaration (like Blue in enum Color { Red, Blue, Green };, another variable declared constexpr, and so on.

They might be evaluated when all its arguments are constant expressions and the result is not used in a constant expression, but that is up to the implementation.

When is a constexpr evaluated at compile time?

When a constexpr function is called and the output is assigned to a constexpr variable, it will always be run at compiletime.

Here's a minimal example:

// Compile with -std=c++14 or later
constexpr int fib(int n) {
int f0 = 0;
int f1 = 1;
for(int i = 0; i < n; i++) {
int hold = f0 + f1;
f0 = f1;
f1 = hold;
}
return f0;
}

int main() {
constexpr int blarg = fib(10);
return blarg;
}

When compiled at -O0, gcc outputs the following assembly for main:

main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 55
mov eax, 55
pop rbp
ret

Despite all optimization being turned off, there's never any call to fib in the main function itself.

This applies going all the way back to C++11, however in C++11 the fib function would have to be re-written to use conversion to avoid the use of mutable variables.

Why does the compiler include the assembly for fib in the executable sometimes? A constexpr function can be used at runtime, and when invoked at runtime it will behave like a regular function.

Used properly, constexpr can provide some performance benefits in specific cases, but the push to make everything constexpr is more about writing code that the compiler can check for Undefined Behavior.

What's an example of constexpr providing performance benefits? When implementing a function like std::visit, you need to create a lookup table of function pointers. Creating the lookup table every time std::visit is called would be costly, and assigning the lookup table to a static local variable would still result in measurable overhead because the program has to check if that variable's been initialized every time the function is run.

Thankfully, you can make the lookup table constexpr, and the compiler will actually inline the lookup table into the assembly code for the function so that the contents of the lookup table is significantly more likely to be inside the instruction cache when std::visit is run.

Does C++20 provide any mechanisms for guaranteeing that something runs at compiletime?

If a function is consteval, then the standard specifies that every call to the function must produce a compile-time constant.

This can be trivially used to force the compile-time evaluation of any constexpr function:

template<class T>
consteval T run_at_compiletime(T value) {
return value;
}

Anything given as a parameter to run_at_compiletime must be evaluated at compile-time:

constexpr int fib(int n) {
int f0 = 0;
int f1 = 1;
for(int i = 0; i < n; i++) {
int hold = f0 + f1;
f0 = f1;
f1 = hold;
}
return f0;
}

int main() {
// fib(10) will definitely run at compile time
return run_at_compiletime(fib(10));
}

How to tell if expression is evaluated at compile time or runtime?

An expression is evaluated at compiletime if the result is assigned to a constexpr variable, used in a static_assert or noexcept statement, or used as a template parameter. This is called a constexpr context.

For example:

// Function which can calculate the fibbonacci sequence at compiletime
constexpr int fib(int n) {
if(n == 0 || n == 1) return n;
return fib(n - 1) + fib(n - 2);
}

int main() {
// This one is calculated at compiletime
constexpr int fib10_at_compiletime = fib(10);

// This one is calculated at runtime
// (unless the compiler was really aggressive when doing optimizations)
int fib10_at_runtime = fib(10);
}

In order to call a function or something at compiletime, it needs to be marked constexpr.

What can you do at compiletime?

C++11:

  • Declare variables (but not modify them)
  • Call other constexpr functions
  • Call constexpr constructors (and default ones)
  • Use carrays and std::array
  • Use static_asserts and stuff
  • typedef and using declarations

C++14 additions:

  • You can also use lambdas now
  • You can modify variables inside a constexpr function
  • you can have constexpr member functions that change member variables
  • you can pass references (the non-const kind) to constexpr functions

C++20 additions: (C++20 is coming out in 2020)

  • You can allocate memory now
  • You can call virtual functions now
  • You can have try-catch blocks

Is std::sort constexpr?

In order to use a function in a constexpr context, it must be marked constexpr (which comes with a set of restrictions on what you can do in the function; these are discussed below). In C++11, std::sort isn’t constexpr because it breaks those restrictions (and it won’t be constexpr until C++20).

However, if you’re allowed to use C++14, you can write your own sorting function that works at compile time.

Full overview: https://en.cppreference.com/w/cpp/language/constexpr

C++ constexpr - Value can be evaluated at compile time?

The quoted wording is a little misleading in a sense. If you just take PlusOne in isolation, and observe its logic, and assume that the inputs are known at compile-time, then the calculations therein can also be performed at compile-time. Slapping the constexpr keyword on it ensures that we maintain this lovely state and everything's fine.

But if the input isn't known at compile-time then it's still just a normal function and will be called at runtime.

So the constexpr is a property of the function ("possible to evaluate at compile time" for some input, not for all input) not of your function/input combination in this specific case (so not for this particular input either).

It's a bit like how a function could take a const int& but that doesn't mean the original object had to be const. Here, similarly, constexpr adds constraints onto the function, without adding constraints onto the function's input.

Admittedly it's all a giant, confusing, nebulous mess (C++! Yay!). Just remember, your code describes the meaning of a program! It's not a direct recipe for machine instructions at different phases of compilation.

(To really enforce this you'd have the integer be a template argument.)

How to determine if a parameter is a constexpr in compile-time

If I understood your question now after the discussion in the comments correctly, you have a operator""_constexpr_str which is marked consteval and returns a string-view-like type constexpr_str_t with some additional information attached based on the contents of the string.

You then have a function taking an (ordinary) string literal as argument with overloads taking either constexpr_str_t or const char*. You basically want the const char* overload to only be chosen when the argument is not a constant expression. Otherwise the constexpr_str_t overload should be chosen and constructed at compile-time (i.e. via consteval) so that the extra information can be attached at compile-time.

This cannot work however, because it is impossible to overload on consteval. Whether or not a consteval function is called depends only on the type of the argument. It is not possible to distinguish between constant expression arguments and non-constant expression arguments.

Trying to determine whether a function argument is a constant expression inside a function is also impossible. Functions are compiled individually. They are not compiled twice depending on whether or not the argument is a constant expression.

The only thing possible is to change behavior based on whether the whole expression that the function call is used in is a context requiring a constant expression. That is what if consteval is for. But if you are making a decision based on such a scenario, you don't need

constexpr constexpr_str_t constexpr_str(str);

You can simply do the calculation for the additional properties of the string there as if at runtime and mark the function constexpr. If used in a context requiring a constant expression it will be evaluated at compile-time.

If you want to enforce this even if the call doesn't happen in a context requiring a constant expression, then it is impossible.

You can however write a macro which tests whether an expression is a constant expression and then conditionally calls either a consteval function or a non-consteval function. It is just impossible through a function call.

How can I confirm that my constexpr expression has been actually performed in compile-time

How can I confirm that my constexpr expression has been actually performed in compile-time

You have to check the resulted assembly.

But you can only check the specific executable, with the specific compiler in the specific platform. You don't have guaranties that, also with different compilers, you get a compile-time execution from the same code.

From the language point of view, you can impose the compile-time execution, but never forget the "as-if rule" that "Allows any and all code transformations that do not change the observable behavior of the program".

To impose the compile-time execution (ignoring the "as-if rule"), you have to use the value returned from the constexpr function where a value is required compile-time.

Some examples (supposing the constexpr foo() function returns a std::size_t):

1) to initialize a constexpr variable

constexpr std::size_t bar = foo();

2) in a C-style array size

char bar[foo()];

3) in a template parameter

std::array<char, foo()>  bar;

4) in a static_assert() test

static_assert( foo() == 3u, "isn't three");


Related Topics



Leave a reply



Submit