问题
The setup:
I have a function that uses SIMD intrinsics and would like to use it inside some constexpr functions.
For that, I need to make it constexpr. However, the SIMD intrinsics are not marked constexpr, and the constant evaluator of the compiler cannot handle them.
I tried replacing the SIMD intrinsics with a C++ constexpr implementation that does the same thing. The function became 3.5x slower at run-time, but I was able to use it at compile-time (yay?).
The problem:
How can I use this function inside constant expressions without slowing down my program at run-time?
Some ideas:
- Adding support for constant evaluating all SIMD intrinsics to the compiler constant expression evaluator, for all compilers: probably the right solution, but an impossible titanic task.
More pragmatic solutions would be to either:
- overload a function depending on whether it is being executed inside a constant expression (that is, provide a constexpr, and a non-constexpr version).
- or, somehow branch inside a constexpr function between the constexpr and run-time implementation (that is, detect in a branch whether the function is being executed inside a constant expression).
Anyhow, I am open to any suggestion that solves my problem.
Hints:
- @RMartinhoFernandes suggested in the Lounge to use
__builtin_constant_p
to detect whether the function arguments are all constant expressions, in which case the compiler would hopefully be at least attempting to evaluate the function at compile-time.
Failed attempts:
- @Jarod42 made the straight forward suggestion of just using two independent functions. I would briefly like to point out why this cannot work because it is not trivial. This solution assumes that at the call-site it is known whether the function will be constexpr evaluated or not. But this is not the case. Consider a constexpr function calling mine, which version of my function should it pick? It must pick the constexpr one in order for it to compile, but that "outer" constexpr function could still be evaluated at run-time. In that case, it would use the "slow" compile-time implementation, and hence, this approach does not solve the problem.
回答1:
I would do it like this
constexpr int doit(int input, bool inconst = false) {
return inconst ? doitconsty(input) : doitfast(input);
}
If the call to doit
is inside of a constexpr
function that can be called to perform something either at runtime or at compile time, just forward the flag
constexpr int f(int n, bool inconst = false) {
/* ... */
int importantInt = doit(n / 42, inconst);
/* ... */
return magicResult;
}
Any constexpr
evaluation has something where it starts, if I'm not mistaken. Pass the inconst
there
enum foo { bar = f(256, true) }
If you are in the runtime world, just call f
like anything else
int main() { std::cout << "test-case: " << f(256); }
It should be noted that this does not work for operators, because you can't add the boolean parameter there. Instead, you could pass the value in some different way, if that's fine for you (for primitive values like int
and bool
, we could not overload the operator either).
template<typename T>
struct maybe_const_value {
T t;
bool isconst;
};
enum foo { bar = maybe_const_value{256, true} % magicTransform };
int main() { return maybe_const_value{265} % magicTransform; }
The operator function can then check input.isconst
and use input.t
as the actual value.
来源:https://stackoverflow.com/questions/42132565/branching-on-constexpr-evaluation-overloading-on-constexpr