Tech News
← Back to articles

C++ Enum Class and Error Codes, Part 3

read original related products more articles

C++ Enum Class and Error Codes, part 3 Last time we explored some commonly found alternatives to using enums for error codes such as asserts, contracts and std::expected. Finally today we consider some unthinkable options.

The code examples given in part 1 and part 2 strongly focused on simple (and I’d argue, quite common) form of error handling: run a sequence of operations, and if any of them fails bail out and report an error. We tried a few alternatives but they all came with caveats. Error code returns polluted a lot of the logic with if ( error ) { return error; } , std::expected was a bit more concise but demanded we rewrite the signature of the functions we call to fit the monadic form and finally contracts allowed for cleaner user code but aren’t really made to do custom error handling (if they make it into C++26 at all) or recoverable errors (like a missing file).

As it turns out, there is a way in C++ to write a function or block expecting everything will succeed, and then through some compiler magic have the control flow bail on error and return, while still running any destructor that needs to be run. In fact it’s been there since the start. We could just have the functions we call throw an exception if they fail.

Exceptions? Really?!

Hear me out for a second. On paper this seems to fit the bill. Let’s look at what it would look like:

Model load_model ( const std :: filesystem :: path & path ) { const auto blob read_from_path ( path ); const auto asset = parse_gltf ( blob ); return upload_to_gpu ( asset ); }

For the implementer of load_model() , this is about as concise and clean at it could be. We write as if no error could happen, focusing on the happy path. And for the caller of load_model() , they can either add a try / catch block to handle failures, or let it pass through to our global exception handler (or call std::abort() if we don’t have one). In this case the caller should probably catch and handle it, as loading assets is the kind of thing that should be allowed to fail (although it should be loud about it so it gets fixed).

We could even one-line it like this:

Model load_model ( const std :: filesystem :: path & path ) { return upload_to_gpu ( parse_gltf ( read_from_path ( path ) ) ); }

Now for the drawbacks…

... continue reading