@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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With