I am using the neat fmt library, which in its version 8, does compile-time checking of its format string if the compiler supports the relevant features.
I would, at some point, like to write the following code:
throw my_exception("error: {}", 123);
Sadly, the naive implementation:
struct my_exception : std::runtime_error {
template<typename... Args>
my_exception(Args&&... args)
: std::runtime_error{fmt::format(std::forward<Args>(args)...)}
{ }
};
fails, as this looses the "consteval-ness" of the string literal argument, which is required by fmt::format
.
For now, I settled on the following:
template<std::size_t N>
struct literal {
constexpr literal(const char (&str)[N]) noexcept {
std::copy_n(str, N, this->str);
}
char str[N];
};
template<literal lit>
struct exception : std::runtime_error {
template<typename... Args>
exception(Args&&... args)
: std::runtime_error{fmt::format(lit.str, std::forward<Args>(args)...)}
{
}
};
which gets called like
throw my_exception<"foo {}">(123);
How can I get back a normal function call syntax, while keeping the compile-time checking ?
fmt
but you should decouple the two. I.e. accept a string and do this at the call sitethrow my_exception("error: {}"_fmt(123));
(I don't remember the syntax exactly) – Sisley