我正在使用neat fmt库,其第8版可以在编译器支持相关功能时对其格式字符串进行编译时检查。
我希望有一天能编写以下代码:
throw my_exception("error: {}", 123);
可悲的是,这种天真的实现:
struct my_exception : std::runtime_error {
template<typename... Args>
my_exception(Args&&... args)
: std::runtime_error{fmt::format(std::forward<Args>(args)...)}
{ }
};
失败了,因为这会失去字符串字面值参数的“consteval-ness”,而这是fmt::format
所必需的。
目前,我采用以下方式解决:
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)...)}
{
}
};
这个函数的调用方式类似于
throw my_exception<"foo {}">(123);
我如何保持编译时检查,同时恢复正常的函数调用语法?
fmt
,但你应该将它们解耦。也就是说,接受一个字符串,并在调用时执行此操作throw my_exception("error: {}"_fmt(123));
(我不记得语法确切的形式)。 - bolov