C++ Error Handling

C++ has two main mechanisms to handle runtime errors:

  1. Exceptions
  2. Error codes
    • extra: with std::optional nullable return type

Exceptions

The most common way of handling runtime errors is using exceptions - with try, throw and catch statements.

Exceptions work well for desktop applications but have certain issues which make it undesirable for writing real-time code running on embedded hardware (e.g. medical devices or vehicle software).

Issues

  • Breaks codeflow by potentially creating multiple exits
  • Performance overhead - takes non-deterministic time for stack unwinding
  • Can cause resource leaks, especially when calling legacy C++ code with raw pointers
  • Requires heap memory allocation - which is usually a no-no for embedded standards compliance (e.g. AUTOSAR)

Many companies have a no exception policy.

Error codes

Returning error codes are a legacy from C. A function would return a sentinal value indicating an error and the caller is responsible for checking this.

Some common patterns:

bool func1() { return true; }
int func2() { return 2; }

enum class ErrorCode { SUCCESS = 1, INVALID_INPUT = 2}; // expressive + type-safe
ErrorCode func3() { return ErrorCode::SUCCESS; }

Issues

  • At the call-site, a if..else chain is needed to handle errors
  • Return codes can be silently ignored

+ std::optional

🏗️

std::optional<T> was introduced in C++17 and expresses nullability (“either a <T> or nothing”). So the function can either (a) return null if there was an error or (b) the return value. However, we would lose information on the type of error.

Comments