Keeping consteval-ness of function arguments
Asked Answered
K

1

4

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 ?

Kohinoor answered 16/10, 2021 at 13:39 Comment(2)
My advice: don't force your exception to use a particular format library. I love fmt but you should decouple the two. I.e. accept a string and do this at the call site throw my_exception("error: {}"_fmt(123)); (I don't remember the syntax exactly)Sisley
nah, this is application code, I just want it to be as simple as possible. decoupling it would really be a YAGNI, I've been using fmt since 2014 and don't plan to change (and if I do I'll just apply a refactor to the whole codebase. But I really doubt I'd go for something that would use a different expression syntax than fmt anyways).Jame
L
9

In {fmt} 8.0 and later you can do this by using the format_string template that, as the name suggests, represents a 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)...)) {}
};
Linstock answered 16/10, 2021 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.