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,478
  • 1
  • 13
  • 30
  • 2
    As I understand the rationale behind `die`, programming languages like Perl and PHP create individual web pages by running a script in a process. Writing `die` essentially means "The web page has been sufficiently rendered, I no longer have any use for you, script." It's hard to imagine there being much benefit to such a mechanism in C++ when C++ is not commonly used to build web pages, especially when so much attention is paid to managing memory gracefully in C++. – Robert Harvey Jun 27 '22 at 18:13
  • 2
    @RobertHarvey: `die()` predates the WWW... it's not an HTTP-specific or even networking-specific construct. `die()` is for a process on a Unix-like system. – einpoklum Jun 27 '22 at 18:45
  • Same rationale in any case. "I no longer have any use for this process." It's a good thing well-behaved operating systems clean up after processes' allocated memory automatically. – Robert Harvey Jun 27 '22 at 19:09
  • 1
    @RobertHarvey: So, why is this hard to imagine? Isn't it the same rationale as exit(), terminate() and abort()? – einpoklum Jun 27 '22 at 19:12
  • Are those things really widely used? Seems like the "abandon hope, all ye who enter here" approach. `goto` still exists; doesn't mean it's a good idea. – Robert Harvey Jun 27 '22 at 19:15
  • @RobertHarvey: 1. Sure they are... 2. Why would you `goto` someplace which exits, when you can just exit? – einpoklum Jun 27 '22 at 19:34
  • Is this a question, or a debate? If it's a debate, there are better places on the Internet for that sort of thing, like Reddit. – Robert Harvey Jun 27 '22 at 19:48
  • The question is closed now, but it doesn't seem a good idea to try to make languages looking more like natural languages. And abruptly aborting a C++ programme like that might not be a good idea if using RAII if some system resources would be at risk of not being released as expected. The C++ core guidelines say ["***if you can't throw an exception**, consider failing fast*"](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#e26-if-you-cant-throw-exceptions-consider-failing-fast) but this idiom encourages not to consider exceptions in the first place. – Christophe Jun 27 '22 at 22:28
  • @Christophe: It's enough of a good idea to merit 3 explicit standard library functions. But it's true that the stack is not unwound, so that, in principle, allocated out-of-process resources may remain orphaned. – einpoklum Jun 27 '22 at 23:43
  • The existence of something (or even 3 of the same thing) doesn't automatically make it good. Quite the opposite; the fact that there are three ways to do the same thing suggests that none of them is ideal. – Robert Harvey Jun 28 '22 at 02:27
  • 3
    This question is being discussed on [meta](https://softwareengineering.meta.stackexchange.com/q/9390/118878). – Greg Burghardt Jun 28 '22 at 17:57
  • The use of the word `die` is not what you usually see in C++ but there's nothing wrong with it. One nice thing about this kind of fail-fast routine is you can do some interesting things in `die()` just before you exit the process, e.g., emit a stacktrace. But more idiomatic C++ to wrap an `assert` statement with some kind of - typically spelled "assert" but with different _capitalization_ - and use that instead of an if statement. You'd use `Assert(, , )` or maybe even ... – davidbak Jul 16 '22 at 00:04
  • ... `Assert(, ` (where this `Assert` is implemented as a macro) which would look like `Assert(still_looking_good(), "bad thing happened" << foo << ", " << bar")` for variables `foo` and `bar` for which `operator<<(ostream&, T)` is defined. – davidbak Jul 16 '22 at 00:06
  • @davidbak: "assert" suggests that you are certain that the condition will be met; and that you will only check it in debug builds. – einpoklum Jul 16 '22 at 06:58
  • @einpoklum - you're right that `assert` is used for debug builds - consider instead using `verify` or something. But yes, you assert the condition is held, not `if (the bad thing happens)` - i'm just suggesting it is more common in C++, when checking a condition, to specify that it must be true rather than check that it is false. – davidbak Jul 16 '22 at 15:26
  • 1
    By any chance, are you a German speaker and are you referring to `this`? [relevant link](https://www.youtube.com/watch?v=gaXigSu72A4) – Flater Jul 20 '22 at 09:58
  • @Flater: I know just about enough German to say: "Nein, Ich sprechen nicht Deutch" and I'm not even sure that's quite correct... but thanks for die nostalgic clip :-) – einpoklum Jul 20 '22 at 10:37
  • @DocBrown: `or` is perfectly valid C++. – einpoklum Jul 22 '22 at 12:05
  • @einpoklum: I guess you are right. I can't believe I missed that, since I am using C++ now since 1994 and never saw code with "or", only "||" – Doc Brown Jul 22 '22 at 12:09
  • @DocBrown: Weird... Anyway, this actually relates to the question's content - the potential for an idiom which looks and sounds somewhat like a sentence in English. – einpoklum Jul 22 '22 at 12:45

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,478
  • 1
  • 13
  • 30
Caleth
  • 10,519
  • 2
  • 23
  • 35
  • I was thinking more along the lines of the first version, although with [`std::exit()`](https://en.cppreference.com/w/cpp/utility/program/exit). An exception might be caught, and I want `die()` to actually do what it says. But thanks for emphasizing this point. – einpoklum Jun 27 '22 at 10:24
  • @einpoklum `std::abort`, `std::exit` or `std::terminate` would be interchangeable for the purpose of this answer – Caleth Jun 27 '22 at 10:25
  • Fair enough, edited that in. – einpoklum Jun 27 '22 at 10:26
  • Your second `die` just obfuscates `throw things_exception("things not ok", foo);`, and is thus a net drain. (Though it can be used as a callback, and `try` itself cannot.) Also, good on you to fix the format-strings type. – Deduplicator Jun 27 '22 at 10:40
  • @Deduplicator it's a bool expression, rather than a throw expression. You could do similar with `throw blah... , false` – Caleth Jun 27 '22 at 10:52
  • use when exceptions "make you give in and cry"? – Ewan Jun 27 '22 at 14:40
  • @Deduplicator: Yes, I've changed that too. I copied from non-C++17 code. – einpoklum Jun 27 '22 at 19:36
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,285
  • 16
  • 25
  • 1. Why is it "better" to exit using an exception?. 2. In many scenarios, use of exceptions is unacceptable. 3. Sometimes you need to exit while handling an exception. – einpoklum Aug 26 '22 at 10:09
  • 1. because there are situations when you decide to interupt your application flow (exit) but client code may decide to handle the error. Exitting does not allow for that option. 2. I have worked in a few projects like that (not using exceptions because they were unacceptable) - the reasoning for not using exceptions was always bogus (the arguments were superficial). 3. If you need to exit while handling the exception, that should be decided _while handling the exception_, not while raising the exception. Many places that signal an error condition do not know how to handle the error themselves. – utnapistim Aug 26 '22 at 11:50
  • 1. I'm not sure what you mean by "client code". If you mean when writing a library, then I certainly agree, but otherwise - I'm the "client". 2. The reasoning for using exception is not "always bogus"; often, it's not even possible, and there are no stack, no interrupt, etc. 3. So, assume my question is about code in an exception handler. – einpoklum Aug 26 '22 at 13:40
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
  • 10
  • 14