1

If we implement the following function:

template <typename... Ts>
[[noreturn]] inline bool die(std::string_view message_format = "", Ts&&... args);

We can then write:

if (bad_thing_happened()) die("A bad thing happened!");

or, for example

things_are_ok() or die("Things are not ok, and foo is {}", foo);

I know this idiom from perl (which also has unless, making such expressions more natural-language like: die("oh no") unless (things_are_ok())). Is it a good idea to use this idiom in C++?

Note: I'm only asking about code where it makes sense to terminate the application. If std::exit() is inappropriate, naturally so is a die() command.

einpoklum
  • 2,752

3 Answers3

4

I can imagine two implementations of die, and I have issue with both of them

template <typename... Ts>
[[noreturn]] inline bool die(std::string_view message_format = "", Ts&&... args)
{
    std::cerr << std::vformat(message_format, std::make_format_args(args...));
    std::abort(); // or std::exit or std::terminate
}

That is, write the message somewhere and end the program. I could imagine a project using this, but not the projects I'm involved in.

It seems most appropriate in projects large enough that you don't just return up to main, but small enough that killing the whole process is never overkill.

template <typename... Ts>
[[noreturn]] inline bool die(std::string_view message_format = "", Ts&&... args)
{
    throw std::runtime_error(std::vformat(message_format, std::make_format_args(args...)));
}

I.e. throw a generic exception, with the message. I would prefer some granularity in the type of the exception, so that args... could instead be a data member for the catch. If pressed, i'd write that as

template<typename T, typename... Args>
[[noreturn]] inline bool die_with(Args&&... args)
{
    throw T(std::forward<Args>(args)...);
}

things_are_ok() or die_with<things_exception>("things not ok", foo);

But there are other ways of massaging a throw to be a bool expression

things_are_ok() or (throw things_exception("things not ok", foo), false);
einpoklum
  • 2,752
Caleth
  • 12,190
1

Is it a good idea to use this idiom in C++?

No.

Exit (or terminate, or abort, or die) is not something you should use in C++. There are cases when it is useful, but those are special cases (special as in "we are working at a nuclear power plant and need to guarantee the application is exited NOW - as in RIGHT NOW DAMMIT!").

To interrupt process flow in a normal application, it is better to throw an exception.

utnapistim
  • 5,313
1

In library code, no. Library code should cause the application to terminate only on the most severe logic errors, something that should never happen (like memory self-corrupting). And if you do this termination, you should at least leave behind a core dump. Even then, throwing an exception specifically designed for severe logic errors wouldn't be a bad idea though, because an unhandled exception will anyway terminate the application, and presumably that exception wouldn't be handled anyway (if bits are flipping randomly, there's no sane way to handle the exception -- on the other hand, if bits are flipping randomly it could be that some exception handler flipped randomly to handle your severe logic error -- but there's also the problem that if bits are flipping randomly, you can't trust your core dump).

However, in application code, only the application author knows. In complex applications, where lot of the code is actually per-application library code, you should probably consider that code similar to generic library code. However, in simple <1000-liner applications it's possible that your error handling strategy is simply exiting. And even if that strategy changes at some point of time, you shouldn't have too much code to rewrite. In any case, some "die" function is far better choice than printing and error message and exiting, because you can easily search for all users of "die" and change them to throw an exception instead -- or maybe you want to just edit the "die" function to throw an exception.

juhist
  • 2,579
  • 12
  • 14