4

@cyberpunk_ is trying to achieve something and made some questions about it but all the chase boils down to this:

Is it possible to build a tool to enforce compile-time evaluation of a constexpr function?

int f(int i) {return i;}
constexpr int g(int i) {return i;}

int main()
{
    f(at_compilation(g, 0));
    int x = at_compilation(g, 1);
    constexpr int y = at_compilation(g, 2);
}

In all situations, at_compilation enforce compilation-time evaluation of g.

at_compilation doesn't need to be in this form.

Requirements

  • Allow any (numerical native) literal type as input for the constexpr function.
    • this could also be hardcoded based on the function arguments types.
  • Allow any (numerical native) literal type as output, which is the result of the constexpr function call.
    • this could also be hardcoded based on the function return type.

Desirables

  • Reduced macro usage but don't be afraid of using.
  • Be general (not type hardcoded).
  • Support any literal type. At last any numerical native literal type is a requirement.

Related Questions:

  1. When does a constexpr function get evaluated at compile time?
  2. Forcing a constant expression to be evaluated during compile-time?
  3. Passing any function as a template parameter?
  4. Where in the C++11 standard does it specify when a constexpr function can be evaluated during translation?

Answers with relevant code samples:

  • 1
  • 2
  • 3 (this one has an illustrative AT_COMPILATION macro)

All the code samples have limitations regarding the requirements.

A clear explanation for how this is unfeasible in C++ is also a good answer.

I suspect it's impossible based on @K-ballo / @Herb Sutter answer which states "and the result is used in a constant expression as well". This was not part of my former conception about constexpr functions, I firstly thought that just passing literals (or other compile-time input) as arguments would suffice to guarantee (by standard) it to be evaluated at compilation-time.

It's already assumed constexpr function's purpose is that they can fit in constant expression situations when necessary, like in array bounds. That's OK. Given that, this question is about a hack on using them just as a tool for compile time calculation. Whether it's a good or bad thing to do should not matter.

Community
  • 1
  • 1
oblitum
  • 11,380
  • 6
  • 54
  • 120
  • 1
    I do not think it is possible. A _constexpr_ will be evaluated at compile-time based not only on its arguments but on the context where its used, and there are no context-dependent expressions in _C++_. – K-ballo Jan 13 '13 at 23:05
  • 4
    For any purported compile-time constant integer expression `f()`, you can always use `typedef char dummy[f()];` and then get the value as `sizeof(dummy)` or `std::extent::value`. – Kerrek SB Jan 13 '13 at 23:14
  • 1
    @KerrekSB: the value must be positive... – aschepler Jan 13 '13 at 23:21
  • 1
    @aschepler: That's true. I guess adding suitable casts involving `make_unsigned` and `decltype` could work around that limitation... – Kerrek SB Jan 13 '13 at 23:24
  • Why the lambda trick is not good enough? It's impossible without macro anyway. – zch Jan 14 '13 at 00:00
  • @zch, It fails at `constexpr int y = at_compilation(g, 2)`. That trick blocks chaining of `constexpr` when desired. – oblitum Jan 14 '13 at 00:04
  • 2
    Even if the compiler evaluated the expression at compile-time, it is usually still allowed to re-evaluate it at runtime (and check that 2+2!=3 every other instruction, for fun). – Marc Glisse Jan 14 '13 at 00:59

3 Answers3

3

I believe that it's impossible because the compiler is only required to compute values that are used at compile-time, and there is no generic expression that can use every part of a value of class type. Computations that initialize private members might even be impossible to force, as you would depend on a public constexpr member function to use the result.

If you could access the object representation by

static_cast< char const * >( static_cast< void const * >( & const_value ) )

then it would be possible to checksum the result of the computation (and use the result as an integral constant expression), forcing the compiler to perform every calculation that isn't moot. But the cast from void * to char * is disallowed in a constant-expression, and likewise attempting to accomplish the same with a union. Even if it were allowed, if the constructor left one byte uninitialized, using an uninitialized value is also forbidden in a constant-expression.

So, even if C++ had better tools for introspection, it would still be impossible to recover the work performed by a constexpr function in order to artificially use some members but not others.

Just to be clear (even if it repeats the question), there's no reason to want this. The language already requires a check that everything can be computed at compile time, if needed, and the only effect of forcing the compiler to non-lazily compute pure values would be to make it slower and use more memory.

Edit (question was radically altered)

If you have several functions returning scalar type, and want to ensure that some of them work as constant expressions under certain arguments, then write test cases using static_assert.

constexpr int g(int i) {return i;}
int i = 5;
static_assert( g( 3 ) == 0, "failure 1" );
static_assert( g( i ) == 5, "failure 2" );

If you don't want to fix the result values, then discard them. (Unfortunately, GCC may optimize out the non-constant part of such an expression, so you might need to do something more baroque on that platform.

static_assert( g( i ) == 5 || true, "failure only if not constexpr" );

As for encapsulating this into a macro, the other linked questions seem to address a lot. If you want to expand one of those answers or to fix a particular bug, it would be better to explain the bug rather than ask us to read so much literature and start from scratch.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Although it has a few points that may matter for the *actual question*, I see it has multiples points that don't. I see too much thing that is present for not directly addressing from where the possibility of it breaks down (given all the constraints present). That's the only thing that matters (at last for me, and I guess in a strict way too). – oblitum Jan 15 '13 at 02:58
  • @chico **1.** The question only makes sense if we define what "function evaluation" means. In this context that means using all the values computed by a function. **2.** Can we use all the values contained by an object of class type? **3.** The strategies that work at runtime don't work at compile time. **4.** No more possibilities; it's impossible. — If you feel something is left unanswered, or have more questions, please ask. If you see a shortcut to cut this answer down, please write your own answer. – Potatoswatter Jan 15 '13 at 03:02
  • What's the purpose of `constexpr` functions? They fit both in runtime as in compile time, it's a strategy that can be employed to initialize a ordinary variable at runtime as for initializing an array size, provide you know the constraints that apply for the arguments. The question asks for that, the constraints are already assumed for the arguments, it's just about providing all compile time constraints while packaging it to resemble the syntax that's asked for. It's just that. Given that, I don't see the point of your wording, maybe I need to rephrase the question. – oblitum Jan 15 '13 at 03:20
  • @chico Rephrasing the question *is the problem.* When I read answered it, it didn't mention "numerical native". Yes, you can certainly evaluate and discard a scalar number at compile time: `static_assert( g(0) == 0 || true, "" );` Is that seriously all? – Potatoswatter Jan 15 '13 at 03:29
  • If you had saw my answer, the illustrative AT_COMPILATION macro, you may have got a better idea of this craziness =) – oblitum Jan 15 '13 at 03:32
  • `constexpr` does a lot more than initialize array bounds from expressions with functions. You can initialize an entire compile-time hierarchical data structure, and compile-time navigate it to generate other things including `switch` statements, statically-initialized data, and array bounds. – Potatoswatter Jan 15 '13 at 03:34
  • @chico If it's essential to the question, it should be given and explained, not linked from a footnote. And I don't see the point of it. It's a convenience wrapper that doesn't add anything semantically to just writing out a function call. If you want to ensure that something is compile-time, for testing purposes, then `static_assert` is the correct tool. – Potatoswatter Jan 15 '13 at 03:37
  • All nice, but wanting to make constexpr functions to eval at compile time just with a short call syntax seems a complete impossibility. You aways need to set a constexpr variable in a previous line. That's what @cyberpunk_ was trying to eliminate, and I just got curious george about it. – oblitum Jan 15 '13 at 03:39
  • @chico No need to define a `constexpr` variable, pass a literal! If you have a specific bug in our `AT_COMPILATION` macro, then put the source code into a dedicated question and *describe the bug*. Don't get all theoretical and make an XY problem, then expect a specific fix for `AT_COMPILATION`. Ask a theory question, get a theory answer. – Potatoswatter Jan 15 '13 at 03:48
  • I'm sure I've not radically changed the answer, just reduced the type scope a bit more. From my understating, you seem to have interpreted I was asking about evaluation of any function, but I've warned this was about constexpr function from beginning, and as such, a lot of constraints apply. They're for compile time evaluation already. – oblitum Jan 15 '13 at 03:52
  • Also static_assert is not the aim, just enforce by enforcing is not the aim, the purpose is to have the return to print or do anything with it. Also, about just pass the literal... I advice to read all the links I provide, including comments. Question 1, both at isocpp as in stackoverflow is crucial. – oblitum Jan 15 '13 at 03:53
  • @chico **`constexpr` applies to objects and return values and class type,** and the types to which it applies are called **literal types**. It now appears coincidental that you used such terminology to describe what you wanted, but how was I to know that? You reduced the scope so much as to make it a completely different question… and you still need to reduce the scope further by posting `AT_COMPILATION` and describing what you want it to do. Providing these extensive references is not part of the Q&A process, and we're not responsible for reading all those pages to divine your real question. – Potatoswatter Jan 15 '13 at 03:55
  • I think it's good enough as it is, you can downvote it because of that if you think it deserves it =) – oblitum Jan 15 '13 at 03:59
  • s/to have the return/to have the constexpr function return/ – oblitum Jan 15 '13 at 04:01
  • I guess you won the big prize. Thanks, @cyberpunk_ will like this. – oblitum Jan 15 '13 at 04:09
  • Hey, `|| true` couldn't induce an optimization that would even remove `o` out of the analyzes? Also even the former expression couldn't be optimized to just `true`?... I'm unsure whether this can or not happen and so eliminate its usefulness. – oblitum Jan 15 '13 at 04:13
  • nice try =), but `enforce_constexpr` must be `constexpr` itself for being able to initialize a constexpr variable. – oblitum Jan 15 '13 at 04:19
  • hmmm, I first tried clang at my machine, I guess we found a compiler error, http://liveworkspace.org/code/1t5Gpo$0. I thought the code was ok, the error was pointing for something that at instantiation should be ok, and it's with gcc indeed, but I'm still unsure about the analysis elimination by *aways true*. – oblitum Jan 15 '13 at 04:25
  • ok, http://liveworkspace.org/code/1t5Gpo$1, it seems gcc is doing the elimination thing in truth... no compiler is working with that, for the purpose. And I'm unsure whether it should be assumed to work. – oblitum Jan 15 '13 at 04:30
  • @chico hmm, it works with `o == 0 || o != 0`. Looks like a compiler bug to me. – Potatoswatter Jan 15 '13 at 04:35
  • it doesn't work, gcc is still passing without checking when using a runtime value and clang presents the same error. – oblitum Jan 15 '13 at 04:41
  • @chico `-O2` seems to trigger it. Unfortunately, this puts you into an arms race with the optimizer. Take comfort in the idea that you're right, and it's wrong… compiling for debug with `-O0` is fairly reasonable, anyway. – Potatoswatter Jan 15 '13 at 04:46
  • Guys, go to the chat for further discussion, please! – markus Jan 15 '13 at 12:53
  • @chico I think he's assuming the asker still wants the function to work on any arguments, like any normal function. Doing it that way defeats the goal of checking that the argument is a constant expression. Note that I'm not trying to make the `static_assert` fire, the diagnostic is that the `static_assert` itself is invalid. – Potatoswatter Jan 16 '13 at 00:18
  • @Potatoswatter, It's invalid to use the value of a constexpr function argument in a static_assert, clang diagnostic is right, It doesn't matter if I call enforce_constexpr, defining it is illegal. – oblitum Jan 16 '13 at 05:31
  • @chico Ah, actually you're right. A constant expression is only produced after the values of arguments are substituted into the function. At the time the function is defined, the expression is not a constant expression and should directly fail. – Potatoswatter Jan 16 '13 at 06:27
  • @Potatoswatter it's like you're expecting your function to work like a macro, or like it has to have an instantiation for each argument value it's used with. It doesn't work like that. A constexpr function argument is an argument that can or not be constexpr. So it's invalid from this point upfront. – oblitum Jan 16 '13 at 06:32
  • @chico Macros don't evaluate anything; they just substitute text. I was treating it like a template. `constexpr` functions do have instantiations in a sense, in that they're memoized, although that's not part of the standard. Anyway, you're probably best off adapting the macro you already wrote. – Potatoswatter Jan 16 '13 at 07:04
3

Thanks to C++17 (lambda constexpr, auto template parameter, inline as valid template non-type value) we now have a solution:

//implementation
#include <utility>

template<auto X>
using constant = std::integral_constant<decltype(X), X>;

template<class T>
constexpr auto to_constant(T f) //should use && but clang has a bug that would make +f fail
{
   constexpr auto ptr = +f; //uses conversion operator to function pointer
   return constant<ptr>{}; //not yet implemented for gcc ("no linkage"), working with clang
}    

#define constexpr_arg(...) to_constant([]{ return __VA_ARGS__; })

//userland
template<auto Func>
constexpr void func(constant<Func>)
{
   constexpr decltype(auto) x = Func();
   static_assert(x == 3.14);
}

int main()
{
   func(constexpr_arg(3.14));
}

proof it's working : https://godbolt.org/g/vWbyjE

Also this version doesn't work for all cases (mainly if the argument of the macro uses non-constexpr values but still produce a constexpr result).

For such uses cases : https://godbolt.org/g/DRZ5JM

For a gcc version (so portable for now):

//implementation
template<class T>
struct constant
{
   static constexpr decltype(auto) value = T::getPtr()();
};

template<class T>
constexpr auto to_constant(T&& f) //remove the && if you want to be also compatible with clang
{
   constexpr auto ptr = +f; //uses conversion operator to function pointer
   struct A
   {
      static constexpr auto getPtr() { return ptr; }
   };
   return constant<A>{};
}    

#define constexpr_arg(...) to_constant([]{ return __VA_ARGS__; })

//userland
template<class Constant>
constexpr void func(Constant&&)
{
   static_assert(Constant::value == 3.14);
}

int main()
{
   func(constexpr_arg(3.14));
}

https://godbolt.org/g/LBCYfi

  • Interesting code!, but sadly I think there may be a misunderstanding. "userland" shouldn't have a special function signature for the expressions being passed as arguments, so that they are enforced to evaluate at compile time, by _extraction_, etc. For example, for `void foo(int)`, one wishes to enforce a compile time constant to be calculated when passing to `foo` in `foo(eval_at_compilation_time(constexpr_bar(1000)))`. If `eval_at_compilation_time(constexpr_bar(1000))` can't be evaluated at compilation time, an error should be generated instead of letting it for runtime. – oblitum Nov 03 '17 at 04:48
  • Ah ya, I misread the question. The solution is much simpler then: https://godbolt.org/g/qT5W89 – Bastien Penavayre Nov 03 '17 at 05:01
  • Of course, `foo([]{ constexpr auto e = expr; return e; }())` works! Does it work only now, on C++17, due to constexpr lambda? This succinct oneliner can be the full answer, no need for the macro helper, etc. – oblitum Nov 03 '17 at 05:16
  • Notice if you follow the question links, in the previous quest, it was indeed attempted to make use of lambdas for this but without luck. – oblitum Nov 03 '17 at 05:20
  • [This didn't work in C++14](https://stackoverflow.com/questions/14294271/forcing-a-constant-expression-to-be-evaluated-during-compile-time#comment19858159_14297002) at the time of this question. The same solution was attempted before. [It is now working behind the `-std=c++17` flag, notice that with `-std=c++14` it still doesn't](https://godbolt.org/g/Q6TpQA), it's due to the fact that lambdas now can be used in `constexpr` context, so it can now be enforced without missing the `constexpr` characteristic in the final result. – oblitum Nov 03 '17 at 05:40
  • actually `foo([] -> decltype(auto) {constexpr decltype(auto) e = expr; return e; }())` would be a more accurate answer I believe. – Bastien Penavayre Nov 03 '17 at 11:02
2

Use std::integral_constant:

int x = std::integral_constant<int, g(0)>::value;
f(std::integral_constant<int, g(1)>::value);

This code will not compile if g(n) is not evaluated at compile-time.

bowser
  • 21
  • 3