4

Similar to the this question. Needed throw a exception with a message using printf-like style instead of string cocatenation or iostreams. Using the C++ 20 Formatting Library:

  throw std::runtime_error { 
    std::format("Critical error! Code {}: {}", errno, strerror(errno)) 
  }; 

But it not feels ergonomic calling format in all exceptions with formatting, can it get better?

Aamir
  • 1,974
  • 1
  • 14
  • 18
Nico Engels
  • 407
  • 2
  • 12
  • 2
    [Consider not putting a message in your exception but actually useful data](https://softwareengineering.stackexchange.com/questions/278949/why-do-many-exception-messages-not-contain-useful-details) – chrysante Aug 02 '23 at 14:19
  • If you are trying to convert error code and error messages from a table to exceptions, I recommend you to use https://en.cppreference.com/w/cpp/error/system_error instead. It has a very nice design if you find a tutorial. – alfC Aug 02 '23 at 20:50

1 Answers1

3

Yes it can!

#include <format>
#include <stdexcept>

class runtime_exc : public std::runtime_error
{
   public:
     template <class... Args>
     runtime_exc(std::format_string<Args...> what_arg_fmt, Args&&... args)
       : runtime_error { std::format(what_arg_fmt, args...) }
     {
        
     }
};

Usage:

  throw runtime_exc { "Critical error!" };          
  throw runtime_exc {                        
    "Critical error! Code {}: {}", errno, strerror(errno) 
  };                                       

If you assemble the message with format in runtime you can use std::vformat. If needed locale you can add another constructor with it as first parameter. Note that std::format can throw.

EDIT: Barry comment, no need to move the format string and forward the arguments.

Nico Engels
  • 407
  • 2
  • 12
  • 2
    The `runtime_exc` itself might be considered unnecessary. Just need a templated function that constructs the formatted string (as this code does, using `std::format`), and either returns the constructed `std::runtime_error` (yawn, boring), or also does the throw. Call site would look like `if (errno) throw_runtime_error("Bad dog, no biscuit! {}: {}", errno, strerror(errno));` – Eljay Aug 02 '23 at 14:07
  • 4
    _Note that std::format can throw._ bad news if it does... – Paul Sanders Aug 02 '23 at 14:59
  • You do not want to inherit `runtime_error`s constructor since you get different behavior with a single string literal: `runtime_exc("T{} does not work")` is an invalid format string for no arguments... but would work here. Was it supposed to work? Better to have a consistent API. – Barry Aug 02 '23 at 18:42
  • 1
    Also, you neither need to `std::move` the format string (it's trivially copyable) nor forward the arguments (none of them are consumed as rvalues anyway). Just `std::format(what_arg_fmt, args...)` is sufficient - and saves a bunch of typing. – Barry Aug 02 '23 at 18:43
  • @Barry I was thinking it would be expensive... not all exception messages would have formatting. But as you said the format string is a basically a `string_view`. It's okay for me, edited... no problem. – Nico Engels Aug 02 '23 at 20:04
  • I don't see how a custom class is better than a function that returns `std::runtime_error` (or better a templated type). – HolyBlackCat Aug 02 '23 at 20:13
  • @NicoEngels It's not a question of expense, it's a question of what the API actually is. You either take a format string, or you don't - having it half/half is not great. – Barry Aug 03 '23 at 13:44