How to Ensure Constexpr Function Never Called at Runtime

How to ensure constexpr function never called at runtime?

In C++20 you can just replace constexpr by consteval to enforce a function to be always evaluated at compile time.

Example:

          int    rt_function(int v){ return v; }
constexpr int rt_ct_function(int v){ return v; }
consteval int ct_function(int v){ return v; }

int main(){
constexpr int ct_value = 1; // compile value
int rt_value = 2; // runtime value

int a = rt_function(ct_value);
int b = rt_ct_function(ct_value);
int c = ct_function(ct_value);

int d = rt_function(rt_value);
int e = rt_ct_function(rt_value);
int f = ct_function(rt_value); // ERROR: runtime value

constexpr int g = rt_function(ct_value); // ERROR: runtime function
constexpr int h = rt_ct_function(ct_value);
constexpr int i = ct_function(ct_value);
}

Pre C++20 workaround

You can enforce the use of it in a constant expression:

#include<utility>

template<typename T, T V>
constexpr auto ct() { return V; }

template<typename T>
constexpr auto func() {
return ct<decltype(std::declval<T>().value()), T{}.value()>();
}

template<typename T>
struct S {
constexpr S() {}
constexpr T value() { return T{}; }
};

template<typename T>
struct U {
U() {}
T value() { return T{}; }
};

int main() {
func<S<int>>();
// won't work
//func<U<int>>();
}

By using the result of the function as a template argument, you got an error if it can't be solved at compile-time.

Constexpr functions not called at compile-time if result is ignored

It might be confusing, but constexpr functions should be called at compile time only in constexpr contexts (assignation to constexpr variable, use for array size or template parameter, ...).

In regular context, functions are called at runtime. Optimizer might resolve that function at compile time (as for any other functions following the as-if rule). constexpr is indeed a good hint for optimization to happen.

You could argue that since constexpr functions cannot have side-effects

They can have side effect, see following valid example:

constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}

int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}

Demo

constexpr error at compile-time, but no overhead at run-time

Is there a way to cause a compile-time error with a constexpr function, but not do anything at run time?

You can use the exact same trick, but instead of using a throw-expression, use an expression that is not a constant expression but does what you want at runtime. For instance:

int runtime_fallback(int x) { return x; } // note, not constexpr
constexpr int f(int x) {
return (x != 0) ? x : runtime_fallback(0);
}

constexpr int k1 = f(1); // ok
constexpr int k2 = f(0); // error, can't call 'runtime_fallback' in constant expression
int k3 = f(0); // ok

Do relaxed constexpr rules in C++1y (C++14) change anything?

Not in this area, no. There are some forms of expression that are valid in constant expressions in C++14 but not in C++11, but neither throw-expressions nor calls to non-constexpr functions are on that list.

How to force a constexpr function to be evaluated at compile time

You could use non-type template arguments:

template <int N> constexpr int force_compute_at_compile_time();

const int a = force_compute_at_compile_time<omg()>();

Since N is a template argument, it has to be a constant expression.

constexpr constructor not called as constexpr for implicit type conversion

Does anyone know of a way I can change my code to get the conversion constructor to be called at compile-time if it can be

As I said in linked posted, call of constexpr functions at compile time is done only in constant expression.

Parameters are not constexpr.

One workaround would be to use MACRO:

#define APPLY_DISPATCHER(dispatcher, str, ...) \
do { \
constexpr callable_type_t<decltype(dispatcher), decltype(make_tuple(__VA_ARGS__))> callable(str); \
(dispatcher)(callable, __VA_ARGS__); \
} while (0)

with

template <typename Dispatcher, typename Tuple> struct callable_type;

template <typename Dispatcher, typename ... Ts>
struct callable_type<Dispatcher, std::tuple<Ts...>>
{
using type = typename Dispatcher::template Callable<Ts...>;
};

template <typename Dispatcher, typename Tuple>
using callable_type_t = typename callable_type<Dispatcher, Tuple>::type;

With usage:

APPLY_DISPATCHER(dispatcher, "one", 1);
APPLY_DISPATCHER(dispatcher, "a", 1); // Fail at compile time as expected

Demo.

But not really better than proposed dispatcher.dispatch(MAKE_CHAR_SEQ("a"), 1); (or with extension dispatcher.dispatch("a"_cs, 1);) (providing dispatch overload to be able to create constexpr Callable).

Is it possible to ensure a constexpr function is called at most once at compile time?

Short answer: no, because constexpr functions cannot read/set external state. (They can have internal state, but they still need to be "pure").


Real answer: probably yes, but it's a bad idea. There is a series of blog posts by Filip Roséen which covers the implementation of stateful constexpr functions by abusing friendship and ADL:

  • "NON-CONSTANT CONSTANT-EXPRESSIONS IN C++" - (cached by Google)

  • "HOW TO IMPLEMENT A CONSTANT-EXPRESSION COUNTER IN C++" - (cached by Google)

  • "HOW TO IMPLEMENT A COMPILE-TIME META-CONTAINER IN C++" - (cached by Google)

The technique is very arcane and complicated. It is considered an abuse of features by CWG, which is trying to make it ill-formed with issue #2118.



Related Topics



Leave a reply



Submit