保持函数参数的consteval特性

3

我正在使用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);

我如何保持编译时检查,同时恢复正常的函数调用语法?

1
我的建议是:不要强制让你的异常使用特定格式库。我喜欢 fmt,但你应该将它们解耦。也就是说,接受一个字符串,并在调用时执行此操作 throw my_exception("error: {}"_fmt(123));(我不记得语法确切的形式)。 - bolov
1
不,这是应用程序代码,我希望它尽可能简单。解耦真的是一个YAGNI,我自2014年以来一直在使用fmt,并且没有计划更改(如果我确实更改了,我将对整个代码库进行重构。但我真的怀疑我会选择使用与fmt不同的表达式语法的东西)。 - Jean-Michaël Celerier
1个回答

8
在 {fmt} 8.0 及以后的版本中,你可以使用名为 format_string 的模板来实现这一点。如其名称所示,它代表了一个格式字符串 (https://godbolt.org/z/bqvvMMnjG)。
struct my_exception : std::runtime_error {
  template <typename... T>
  my_exception(fmt::format_string<T...> fmt, T&&... args)
    : std::runtime_error(fmt::format(fmt, std::forward<T>(args)...)) {}
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接