C++

Lesson 06

Exception boundaries

Catch exceptions at boundaries where code can translate them into a result the caller understands.

Good Code

src/review_loader.cpp
#include <expected>
#include <string>

std::expected<Review, std::string> load_review_result(const std::string& path)
{
    try {
        return parse_review_file(path);
    } catch (const ParseError& error) {
        // Boundary translation preserves the parser failure message.
        return std::unexpected(error.what());
    }
}

Bad Code

review_loader.cpp
#include <optional>
#include <string>

std::optional<Review> load_review_result(const std::string& path)
{
    try {
        return parse_review_file(path);
    } catch (...) {
        // Catching every exception erases the original failure.
        return std::nullopt;
    }
}

Review Notes

What to review

Good Code

The good version catches a domain exception and translates it into an expected error string. The caller still receives the failure reason.

Bad Code

The bad version catches every exception and returns an empty optional. It hides parse errors, file errors, and programming errors behind the same result.

Takeaways

  • Exceptions should travel with meaning until a boundary can translate them into a status, response, or log record.