I want to introduce strong types with error checking at compile time. For my chrono type, I noticed literals are silently narrowed when the underlying type changes from int64_t
to int32_t
, leading to overflows. So I introduced an explicit check.
However this check is not checked at compile time even for constant parameters, like delay_t {10s}
, which cannot be represented.
#include <chrono>
#include <cstdint>
#include <stdexcept>
struct delay_t {
std::chrono::duration<int32_t, std::nano> value {};
constexpr explicit delay_t(std::chrono::duration<int64_t, std::nano> delay)
: value {delay}
{
if (value != delay) {
throw std::runtime_error("delay cannot be represented.");
}
};
};
auto foo(delay_t delay) -> void {}
auto main() -> int {
using namespace std::chrono_literals;
foo(delay_t {10s}); // here I want a compile time error,
// but I get a runtime error.
return 0;
}
This unfortunately compiles and leads to a runtime error. I verified that the literal operator"" s
is a constexpr
and it works using consteval
in the delay_t
constructor. I also want to use the type with runtime values, so that's not an option.
How can I tell the compiler above to evaluate constant literals like time_t {10s}
at compile time? I am using C 20.
CodePudding user response:
constexpr
is evaluated at compile time only in constant expression, not depending of argument.
You might split your expression as
constexpr delay_t delay{10s}; // Compile time evaluated, so error at compile time
foo(delay);
CodePudding user response:
Pass it through a consteval
function.
template<typename T>
consteval T force_constexpr(const T& value) noexcept
{
return value;
}
You can use it like this:
foo(force_constexpr(delay_t {0s})); // No compile-time error
foo(force_constexpr(delay_t {10s})); // Compile-time error