7.04 Standard try-catch Blocks vs std::expected (C++23) for Predictable Real-Time Execution
💻 1. Annotated Source Code
#include <iostream> #include <string> #include <vector> using namespace std; int main() { vector<string> names(5); names.at(0) = "John"; names.at(1) = "Bob"; names.at(2) = "Sally"; names.at(3) = "Karen"; names.at(4) = "Smitty"; for (string name : names) { cout << name << endl; } try { names.at(5) = "Tyler"; } catch (const out_of_range& ex) { cout << "Caught an out_of_range exception: " << ex.what() << endl; } //catch(const exception& ex) <-- would catch ANY exception return 0; }
📐 2. Architecture & UML Class Model
📚 3. Core C++ Concepts Deep-Dive
1. C++ Exception Flow Control
When an exception is thrown with throw, control immediately transfers out of the current block. The runtime searches backwards through active call frames until it finds an enclosing try block with a matching catch clause.
2. Standard Library Exception Hierarchy
All standard exceptions derive from std::exception. Key subclasses include std::runtime_error (unpredictable runtime failures) and std::logic_error (preventable programming bugs like out-of-range indices).
⚡ 4. Embedded Systems & Hardware Reality
1. Real-Time Latency Jitter from Exception Unwinding
In hard real-time control systems (e.g. flight controllers, automotive brake-by-wire, inverter control loops), every cycle counts. An exception throw triggers non-deterministic table lookups across Flash unwind tables, causing latency jitter that can miss microsecond deadlines.
2. Modern Alternative: std::expected<T, E> (C++23)
std::expected<T, E> represents either an expected value of type T or an error of type E. It provides monadic error chaining (.and_then(), .transform()) with 100% deterministic time complexity and zero heap allocation.
💡 5. Production-Ready Embedded Refactoring
Here is how modern real-time systems handle operations that may fail using std::expected (or lightweight custom Result types):
#include <cstdint> #include <string_view> // Embedded Result / Expected Implementation (C++17/20 Compatible) template <typename T, typename E> class Result { union { T value_; E error_; }; bool has_value_; public: constexpr Result(T val) : value_(val), has_value_(true) {} constexpr Result(E err) : error_(err), has_value_(false) {} ~Result() {} constexpr bool has_value() const noexcept { return has_value_; } constexpr const T& value() const noexcept { return value_; } constexpr const E& error() const noexcept { return error_; } }; enum class AdcError : uint8_t { Timeout, OutOfRange, ReferenceVoltageLost }; Result<uint16_t, AdcError> readAdcChannel(uint8_t channel) noexcept { if (channel > 15) return AdcError::OutOfRange; // Read hardware ADC register... return uint16_t(2048); // Success: 12-bit ADC mid-scale reading }
📝 Knowledge Verification Quiz
Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.
std::expected packages values and errors into a tagged union on the stack with deterministic $O(1)$ execution time and zero exception metadata bloat.
std::runtime_error and std::logic_error are defined in the standard <stdexcept> header.
std::terminate().