34

Since the extended versions of constexpr (I think from C++14) you can declare constexpr functions that could be used as "real" constexpr. That is, the code is executed at compile time or can behave as inline functions. So when can have this program:

#include <iostream>

constexpr int foo(const int s) {
  return s + 4;
}

int main()
{
    std::cout << foo(3) << std::endl;

    const int bar = 3;
    std::cout << foo(bar) << std::endl;

    constexpr int a = 3;
    std::cout << foo(a) << std::endl;

    return 0;
}

The result is:

7
7
7

So far so good.

Is there a way (possibly standard) to know inside foo(const int s) if the function is executed at compile time or at runtime?

EDIT: Also is it possible to know at runtime if a function was evaluated at compile time?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
LeDYoM
  • 949
  • 1
  • 6
  • 21
  • afaik you can enforce compile time if you use it eg as a template parameter, eg `template struct bar {}; bar;` – 463035818_is_not_an_ai Oct 24 '17 at 20:38
  • 1
    This is an ongoing issue, unfortunately. I think there are some hacks that sort of work, but there is really no good way to discern whether or not a function is being evaluated in a `constexpr` context. :-/ Jason Turner has talked about this topic at length in a video where he talks about making a JSON parser that runs at compile time. – Omnifarious Oct 24 '17 at 20:39
  • @tobi303 - If you want to force it, you can just assign the output to a `static const` variable and then use that variable instead of the expression. No need to go to all the effort of having it be a template parameter. – Omnifarious Oct 24 '17 at 20:40
  • You can't tell if `const int x` in that context is a constexpr, so no. –  Oct 24 '17 at 20:40
  • Look at the assembly output and see what your compiler did. :-) – Omnifarious Oct 24 '17 at 20:41
  • 1
    A bit ugly, but run it under a `static_assert`. Or assign its results to a `constexpr` object. – WhiZTiM Oct 24 '17 at 20:42
  • Thanks all, but I don't want to "force" it. I want to know. I supose I have to wait as @Omnifarious said. I wanted to be sure there is no way, currently. – LeDYoM Oct 24 '17 at 20:44
  • Omnifarious: Looking to the generated code is a "practical" solution, I can try with an online compiler, but I wanted to know if the language supports that. – LeDYoM Oct 24 '17 at 20:47
  • 1
    @LeDYoM, and if you know, what are you going to do with it? ... `constexpr` functions were designed to fulfill an intent under certain constraints; And there are ways to ensure it does, for example - by assigning it to a `constexpr` variable; if we can't get the results at compile time, we have an error.. Why do you want to know whether it did or not without a compile error. How is it going to be useful to your program? – WhiZTiM Oct 24 '17 at 20:47
  • Related to [Computing length of a C string at compile time. Is this really a constexpr?](https://stackoverflow.com/a/25891133/1708801) – Shafik Yaghmour Oct 24 '17 at 20:49

7 Answers7

31

C++20 introduces is_constant_evaluated, defined in header <type_traits>, which addresses this issue.

constexpr int foo(int s)
{
    if (std::is_constant_evaluated()) // note: not "if constexpr"
        /* evaluated at compile time */;
    else
        /* evaluated at run time */;
}

Note that here the ordinary if is used instead of if constexpr. If you use if constexpr, then the condition has to be evaluated at compile time, so is_constant_evaluated always returns true, rendering the test useless.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • It does not work http://coliru.stacked-crooked.com/a/61f3d3e62656525e – Öö Tiib Mar 29 '20 at 10:09
  • 2
    @ÖöTiib You are calling `foo` in a non-`constexpr` context, so this function correctly indicates that it is evaluated at run time. – L. F. Mar 29 '20 at 10:13
  • So it does not detect if foo(42) is constant expression or isn't merely if I call foo(42) in context of evaluating constant expressions. If foo(42) (after possible overload resolution template argument deduction and so on) isn't constant expression but I call it in context of evaluating constant expression then my program is ill formed, while in code that I posted nothing is ill formed compiler just has opportunity to optimize my foo(42) == 42 out. – Öö Tiib Mar 29 '20 at 10:52
  • @ÖöTiib The question is "Is there a way (possibly standard) to know **in foo(const int s)** if the function is executed at compile time or at runtime?" – L. F. Mar 29 '20 at 11:04
  • There is extended question: "Also is it possible to know at runtime if a function was evaluated at compile time?" It is important question since compiler may optimize run-time evaluation into compile time evaluation based on as-is rule. – Öö Tiib Mar 29 '20 at 11:07
  • @ÖöTiib I guess that won't be easily detectable - after all, the as-if rule is based on the very principle that optimizations should not be allowed if they modify the observable behavior of the program. SFINAE may help with language-level constant expressions, but probably not with optimizations. – L. F. Mar 29 '20 at 11:18
26

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Nir Friedman
  • 17,108
  • 2
  • 44
  • 72
  • 1
    I think you have misunderstood the question. "So my question is: is there a way (possibly standard) to know **in foo(const int s)** if the function is beeing executed at compile time or at runtime?" (emphasis mine). – Johannes Schaub - litb Oct 25 '17 at 11:29
  • EDIT: I didn't spot the follow-up question.. which I think should be a separate question: "EDIT: Also is it possible to know at runtime if a function was evaluated at compile time?". IMO, you should declare in your answer that it only addresses that second issue. – Johannes Schaub - litb Oct 25 '17 at 11:34
  • @JohannesSchaub-litb Ah, you are saying that they want to know, from inside foo, whether it's being evaluated as part of a constant expression? The other answer doesn't address this either, and the example in the question doesn't mention it, which is why I was confused. – Nir Friedman Oct 25 '17 at 13:38
  • Wait.. now that I read it again, he asks "to know at **runtime** if a function was evaluated at compile time?". I'm not sure what that means exactly at all. – Johannes Schaub - litb Oct 25 '17 at 13:54
  • 2
    @JohannesSchaub-litb Yeah, I'm not sure. I also considered the interpretation of literally whether the instructions get executed by the CPU at compile or runtime. It's a bit unclear, it's one of those questions where it would probably be better if OP told us what their endgame was. I think I'll leave this answer up as it's one reasonable interpretation, and it does contain useful information. – Nir Friedman Oct 25 '17 at 14:00
  • So if I understand this correctly, `test_helper` exists because it's a non-`noexcept` function, so if `noexcept(test_helper(expression))` is true, that means it can't be just because the `expression` is `noexcept`; it has to be because the `expression` is `constexpr`. This is pretty smart – Justin Dec 13 '17 at 20:15
  • This `IS_CONSTEXPR` macro seems to only work for expressions; it doesn't work for just variables: https://godbolt.org/g/zHvbBk – Justin Dec 13 '17 at 22:09
  • 1
    @Justin The problem with the variable is that it's not actually used, so the compiler is allowed to say that `test_helper` is a constant expression, even if one of its arguments is not. Offhand I don't see an easy way to truly "use" a generic input, without making additional assumptions. I do think there are also some compiler bugs though: surely this is wrong: https://godbolt.org/g/Gj8iyn. – Nir Friedman Dec 14 '17 at 01:57
  • 1
    Interestingly, if you have `test_helper` take `T` by value instead of universal reference, it appears to work – Justin Dec 14 '17 at 02:42
  • 2
    this broke somewhere between gcc 8.2 and 9.1. prints 000. Clang still 000. MSVC has correct output. – Sopel Aug 21 '19 at 10:02
  • Wait, how can a function be `noexcept` for one parameter value and potentially-throwing for another? Isn't foo simply `noexcept(false)`, period? – einpoklum Apr 13 '20 at 19:26
8

Within a constexpr function, you couldn't tell if you are being evaluated in a constexpr context prior to . Since , this functionalty was added -- constexpr bool std::is_constant_evaluated() will tell you if you are being called in a constexpr context.


Outside a constexpr function, there are a number of ways to determine if a call to a function with a certain set of arguments would be evaluated in a constexpr context. The easiest would be to use the result in a context requiring constexpr.

Assuming your constexpr expression returns a non-void integral or pointer type (including function pointer):

#define CONSTEXPR_EVAL(...) \
  std::integral_constant< \
    std::decay_t<decltype(__VA_ARGS__)>, \
    __VA_ARGS__ \
  >::value

then CONSTEXPR_EVAL( bar(foo, true) ) will fail to compile if bar(foo, true) cannot be evaluated at compile time, and if it can be evaluated at compile time it returns that value.

Other tricks involving noexcept (a function evaluated at compile time is noexcept) can work (see @NirFriedman's answer).

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
7

I think the canonical way to do that is with static_assert. static_asserts are evaluated at compile time, so they will break the build if their condition is false.

#include <iostream>

constexpr int foo(const int s) {
  return s + 4;
}

int main()
{
    std::cout << foo(3) << std::endl;
    const int bar = 3;
    std::cout << foo(bar) << std::endl;
    constexpr int a = 3;
    std::cout << foo(a) << std::endl;

    static_assert(foo(3) == 7, "Literal failed");
    static_assert(foo(bar) == 7, "const int failed");
    static_assert(foo(a) == 7, "constexpr int failed");
    return 0;
}

clang++ -std=c++14 so1.cpp compiles fine for me, showing that everything works as expected.

peteigel
  • 363
  • 1
  • 9
  • Thanks, it worked. To add something: I added the lines int b = 2; std::cout << foo(b) << std::endl; static_assert(foo(b) == 7, " int failed"); and it failed with "non constant expression in static assert". – LeDYoM Oct 24 '17 at 20:56
  • I wish there was a cleaner way. constexpr is still pretty new, by C++ standards, so we are still trying to figure out best practices for using it. – peteigel Oct 24 '17 at 21:16
  • 2
    The problem here is that this absolutely forces the compiler to do everything it can to evaluate the constexpr at compile time. But it says nothing about whether or not the compiler is actually doing it when you call the function in a non-static_assert context. It just tells you that the compiler **could** do it if it wanted to. I've had a compiler (an older version of gcc) not bother to evaluate a `constexpr` function at compile time even though it was perfectly capable of doing so. – Omnifarious Oct 24 '17 at 22:19
  • Okay, I should say that it really depends on the question @LeDYoM is asking. I understood to be "I've got this function. If I call it here, can it be done at compile time?" At some level, as C++ devs, we have to trust that our compilers know best. Using the constexpr-ness of an expression as part of a SFINAE metaprogram is something else entirely. It's a more and more complicated subject the deeper you dive in to it. – peteigel Oct 25 '17 at 03:08
7

If you can use C++20, there is std::is_constant_evaluated which does exactly what you want. std::is_constant_evaluated is typically implemented using a compiler intrinsic.

This is called __builtin_is_constant_evaluated in GCC and clang, so you can implement your own "safe" wrapper around it, even in C++17 and lower.

// if C++20, we will need a <type_traits> include for std::is_constant_evaluated

#if __cplusplus >= 202002L
#include <type_traits>
#endif

constexpr bool is_constant_evaluated() {
#if __cplusplus >= 202002L
    return std::is_constant_evaluated();
#elif defined(__GNUC__) // defined for both GCC and clang
    return __builtin_is_constant_evaluated();
#else
    // If the builtin is not available, return a pessimistic result.
    // This way callers will implement everything in a constexpr way.
    return true;
#endif
}

Note that this builtin is still relatively new (GCC 9.0+) so you might also want to detect the compiler version.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
1

Based on the information in this discussion, I crafted the following minimal example:

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

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

constexpr void test(){
    static_assert(IS_CONSTEXPR(10), "asdfadsf");

    constexpr const int x = 10;
    static_assert(IS_CONSTEXPR(x), "asdfadsf");

    int y;
    static_assert(IS_CONSTEXPR(y), "asdfadsf");
}

int main(){
    test();
    return 0;
}

To my disappointment, It it fails to compile at each of the static_asserts. see https://www.godbolt.org/z/Tr3z93M3s

Robert Ramey
  • 1,114
  • 8
  • 16
0

Sorry for spoiling the party, but there is certainly no standard way of doing this. Under the as-if rule, the compiler could emit code that calculates the result at run time even in such cases where it has already been forced to calculate it at compile time in a different context. Anything that can be done at compile time can be done again at run time, right? And the calculation has already been proven not to throw.

By extension, then, any standard-compliant IS_REALLY_CONSTEXPR or is_really_constexpr check cannot disprove that the exact same call, or for that matter, the value of the exact same constexpr symbol involves a run time calculation.

Of course there usually isn't any reason to repeat a calculation at run time that can be done or even has already been done at compile time, but the question was about telling whether the compiler uses the precalculated result, and there isn't one.

Now you did say possibly standard, so in effect your best bet is probably to test one of the provided solutions with your compiler of choice and hope that it behaves consistently. (Or reading the source code if it is open/public source and you are so inclined.)

Arne Vogel
  • 6,346
  • 2
  • 18
  • 31