C++ Zen of the Sad Path

Epitome of a C++ exception. Jester Box.

This is not an article about C++ being on the sad path, I just simply would like to show and explain clearly the key paradigms in regards to error handling, multiplied by some personalities.

But why not just learning the official consensus recommendation? Simply because there is no such thing.

C++ Illuminati? Proud, Stack Overflow 200k+ status flag carriers? They will never tell you why is this or that error handling the one you should follow. WG21 and succinctly named “subcommittees”? Nope. Ok, let us “just” use std lib, as advised. std lib has them by dozens, error handling idioms that is. How and when any of them should be used? Which one to choose and use? There is no official advice officially underwritten. <system_error>? std::exception? Wait, maybe std::errc? Follow the <filesystem> error handling paradigm? Is std::expect official? Should we wait for what Herb seems to be pushing as std::error? Bjarne seems decisively not in favor …

What could possibly go wrong? ™

Everything you did not want to know about error state handling emanates from here. Here is our simple use case, we shall use:

What a devious, divisive, short piece of C++ code that is. Fine. If that is C++, why not using new then? Because we do not want std::bad_alloc to surprise us. We want total control of our destiny if and when something goes wrong, that is when there is no enough heap to allocate and return a pointer to that allocated, nicely cleaned contiguous block of memory.

All the possible outcomes caused by that function might be joyously categorized, joyously for this bitter pill to swallow easier. Let us form some kind of scale in regards to the state of mind of the developer or even decision-makers on that project:

State of mind v.s. error handling paradigms

  • Delirious: Nothing will ever go wrong!

  • Optimistic: Most of the time all will be fine?

  • Pragmatic: Let us prepare for no memory event.

  • Pessimistic: Most of the time there will be no enough memory.

  • Dark: When there is no memory there is no reason to live.

“That is not enough, where is the main ?!” Jumps in the clever onlooker, readily. Well, a dear member of the audience, the main() reveals the state of mind.

Delirious: Nothing will ever go wrong!

Ok, there is no even free() up there. Why should there be, some people might (and do) ask. And they proceed: OS will clear that up, after the process exits. No need to worry.  I might call that: Happy go lucky software design. Always happy and always lucky software designers method. But is that so outlandish as you are led to believe? Is this a real disaster in the form of C++ code?

Well on your one-man-band “C++naut” desktop that is certainly a moot point, that might be plain wrong or interesting. But definitely not so much inside some (god forbid) pace-maker. Or (god forbid) a large passenger airplane system. Catch my drift? As long as there is no requirement you are “free not to free”, the memory you have asked run time to allocate that is. And as long as your program is not endangering people and livestock well-being. Just please keep it on your machine in any case

Using that little program might even be a  good (sobering) exercise. Execute the above program from some script your OS supports, in one very long loop. And then see what happens. You might even report your findings in some “Happy go lucky” blog.

You will learn every executable run in some kind of run-time environment. Run-time environments are sometimes very forgiving and sometimes are not. And writing software for a living is a situation where what you write will be executing mostly in the second group.

Optimistic: Most of the time all will be fine!

That was not written by a delirious person but there are simply no error checks whatsoever up there. The optimistic developer simply does most of the things right but never checks for errors.  The key assumption is here: “most of the time all will be fine”. For example: “Most of the time there will be enough memory”.

I (in turn) might assume some serious people who can show empirical results from some very deep and long tests, statistically proving that “most of the time” might be some surprisingly large percentage. Thus there you have it: the case for optimistic no-error-checking software.

Although I can not imagine why anyone would use or buy such software?

It very well might be people simply instinctively write the code which does not allocate a lot of memory in one go. Still, that is a very big speculation. Even if using std lib constructs one makes the optimistic version more resilient:

Will that work? Same as the delirious version it will work. Until it crashes. True, that is a bit more responsible code but certainly a pretty reckless one in the same time. No mission-critical component or system can be written in this “optimistic” state of mind. Imagine e.g. Linux kernel designers assuming there will be enough memory “most of the time”. The Kernel will be crashing in a completely unpredictable fashion. All together with the program, you are using at the moment of the crash, your desktop, your Linux Shell, and even your machine. One should be extremely optimistic to use OS built on top of the optimistic kernel.

Pragmatic: Let us prepare for no memory event.

Ok, that seems very sober design. Happy path, sad path, exceptions, and even <iostream>. Pragmatist writes C++ in the belief it works and behaves as it is advertised. The pragmatist delivers software using the programming language he is asked to use. No time for going to Sunday masses of the church of the divine “no exception” or attending the self-support groups of “never use std::iostream”.

Actually, I am pretty sure, the true pragmatist will do this:

Human software users of the MS Word’s of today certainly could not care less about any of that. You know the kind of people that actually pay for the licenses. Or go to the Apple store and ask what is best for them. I am not diminishing them. People have jobs to do and no time for much else besides their families. That is, I might dare to guess, approx 99% of the users out there.

The one issue C++ has here is competition. It is now almost 2021. Perhaps close to 90% of your everyday consumer GUI apps on tablets, phones, and watches are not written in C++. Nor it ever will be. There is a very (very) a small number of C++ pragmatists asked to deliver pragmatic approach GUI fronted, C++ code. C++ is being moved “down bellow” or should we rather say “expelled to the Claudy pastures”, to millions of the servers of today’s enormous data centers global networks. And over there C++ pragmatism is not the ruling religion.

Pessimistic: Most of the time there will be no enough memory.

This is the territory where you as the C++ coder are on the very bottom of a very vicious “survival-of-the-fittest” chain. Naturally, you are pessimistic about entering that jungle. Hence this Sun Tzu influenced “defensive programming”:

Primary objective: there must be enough memory always.

And each memory allocation is very likely to fail. My problem with that approach is over-engineered defenses. To develop and test and use effectively (if ever) that kind of software components cluster, surely must take a lot of effort and money.

And yes, in case you have not been pessimistic enough, the data center monitoring system will notify the administrators. And that will teach you to never (ever) mess with the administrators. As they will call you for sure, and you are contractually obliged to answer that call even at 3 AM. Actually, your boss will call you as they will call him, at 3 AM.

Dark: When there is no memory there is no reason to live.

If there is no memory ye shall not return!

That software will crash if there is no enough memory. Contrary to the “Delirious” approach we know where and when exactly it will do that. And there is this “magical seven” symbolic constant that surely has some deep meaning.

In these kinds of apps, anything can deliberately crash. There are systems (e.g. Erlang) where component design philosophy works exactly like that: crash fast and let the run time environment take care of the rest. The obvious question being: what if the run time crashes?

I can (and will) add some more content to this post soon, but for now:

Conclusion: There is nothing wrong with you

Go back to your happy C++ life. Just please whatever the “C++ Illuminati” tell you, take it with a pinch of salt. There is no sad path or right path in error handling. There are only the project requirements. And on that project, you might be the navigator. You are responsible for charting the right path in tune with the requirements. But:

You can not satisfy everyone at the same time

All five personalities will pop-up sooner or later. There is no way to satisfy them all. That is where these “sad path / happy path” ideas are coming from as some explanations have to be developed for whoever can make the decision about the approach you are required to make. You need to know there is no paradigm to cover all the paths and at the same time satisfy all the personalities, in and around the project.

That includes also your peers and that includes not your peers, a.k.a. “decision-makers”.

Right path is not the easy one
The right path is not the easy one

2 thoughts on “C++ Zen of the Sad Path”

Comments are closed.