Every way to report an error
Exceptions, std::optional, std::expected, std::variant, and the error codes they replace. Which one fits which kind of failure.
Ask how ordinary the failure is, and whether the caller needs to know why.
- Rare, and the code that notices it can't handle it (often several calls below the code that can), or a constructor that must refuse → throw an exception.
- "No result" is a normal answer, and the reason is obvious
(a search that finds nothing) →
std::optional. - Failure is an ordinary outcome and the caller needs the
reason, or it happens often in a loop, or the code is built
without exceptions →
std::expected. - Several kinds of legitimate result, none of them a failure →
std::variant. That's not error handling at all, but it's the tool people reach for by mistake.
Don't throw for ordinary control flow, and don't return an
optional when the caller will immediately ask "but why?" Mixing
tools in one program is normal.
Exceptions
Section
Throwing and catching
The error travels up the call stack to the nearest matching catch.
Section
Stack unwinding and noexcept
Why RAII makes exceptions safe, and the promise that a function never throws.
Results in the return type
Choosing, and the neighbor that isn't an error
The four-row table this cluster's decision list comes from.
The Old Way, and the C boundary
A return of 0 or -1 and a result written through a pointer, and what replaces them.
Finding out it went wrong at all
Section
Tests for the error paths
An error path no test reaches is an error path nobody has checked.