I was wondering if it were possible to have a function do different things depending on whether or not it is evaluated at compile time.
For example,
struct Vec4 {
union {
__m128 simd_data;
float data[4];
};
inline constexpr Vec4 operator+(const Vec4 &v) const {
// If at compile time:
return {data[0] + v.data[0], data[1] + v.data[1], data[2] + v.data[2], data[3] + v.data[3]};
// If at runtime
return {_mm_add_ps(simd_data, v.simd_data)};
}
};
int main() {
constexpr Vec4 v1 = {1, 2, 3, 4};
constexpr Vec4 v2 = {3, 1, 2, 5};
// Runtime variable, calls the non-constexpr compiler intrinsic
Vec4 v_a = v1 + v2;
// Compile time constant, runs the sequential additions
constexpr Vec4 v_b = v1 + v2;
}
Being able to do this would allow for the function to both be evaluated at compile time if necessary, as well as using the parallelized SIMD instruction at runtime, bettering the runtime performance. Without this ability, the function has to be evaluated at runtime if using the SIMD compiler intrinsic.
I found a thread referencing SIMD instructions at compile time, however, the only answer suggests using an extra boolean argument to tell the function to run at compile time. This is not possible for operator overloading and thus did not help. I also found another thread which would suggest using a technique similar to this:
template <int>
using Void = void;
template <typename F, typename A>
constexpr auto
is_a_constant_expression(F &&f, A &&a)
-> decltype((std::forward<F>(f)(std::forward<A>(a)), std::true_type{})) { return {}; }
constexpr std::false_type is_a_constant_expression(...) { return {}; }
struct Vec4 {
union {
__m128 simd_data;
float data[4];
};
inline constexpr Vec4 operator+(const Vec4 &v) const;
};
struct StaticStruct {
static constexpr Vec4 add_vec4_constexpr(const Vec4 &v1, const Vec4 &v2) {
return {v1.data[0] + v2.data[0],
v1.data[1] + v2.data[1],
v1.data[2] + v2.data[2],
v1.data[3] + v2.data[3]};
}
static inline Vec4 add_vec4_runtime(const Vec4 &v1, const Vec4 &v2) {
return {_mm_add_ps(v1.simd_data, v2.simd_data)};
}
};
#define IS_A_CONSTANT_EXPRESSION(EXPR) \
is_a_constant_expression( \
[](auto ty) -> Void<(decltype(ty):: \
EXPR, \
0)> {}, \
StaticStruct{})
#define MY_MIN(...) \
IS_A_CONSTANT_EXPRESSION(StaticStruct ::MyMin_constexpr(__VA_ARGS__)) ? StaticStruct ::MyMin_constexpr(__VA_ARGS__) : StaticStruct ::MyMin_runtime(__VA_ARGS__)
inline constexpr Vec4 Vec4::operator+(const Vec4 &v) const {
return IS_A_CONSTANT_EXPRESSION(StaticStruct::add_vec4_constexpr(*this, v)) ? StaticStruct::add_vec4_constexpr(*this, v) : StaticStruct::add_vec4_runtime(*this, v);
}
However, I could not understand why this should work (*for normal functions) as well as why it does not work in this case. The error I get is relevant to capturing this
inside of a lambda, however I do not entirely understand why a lambda is needed in this case and am wondering if there is a better way of doing this.