Object-Oriented Programming

Errors & Exception Handling

Michael L. Collard, Ph.D.

Department of Computer Science, The University of Akron

Error Handling Choices

  • return error code, check when called
  • parameter error code, check when called
  • global state

Error Return

Error Parameter

Global Error Value

Problems with Error Codes

  • Do not work with constructors, as constructors do not return a value
  • Error codes do not have consistent values across applications
  • Numeric results are not obvious, and name forms are often used/needed
  • Propagating an error code is tedious

Error Propagation Flow

Exception Handling by Keyword

  • throw
    • Where error is detected, the function/method immediately returns from that point in the code
  • catch
    • Layered calls are returned (stack unwinding) until one that matches is found
  • try
    • The catch is associated with the code in the try block

Exception Example

Semantics

  • resumption semantics When the code generates an exception, go to where caught, process catch, then return to the original point in the code
  • termination semantics When the code generates an exception, go to where caught, process catch, and continue in the code at the catch point
  • C++ only supports termination semantics

How to use Exceptions

  • Separates the “good path” (or “happy path”) from the “bad path”
  • Errors where the code cannot handle the error locally, e.g., a constructor

  • exception-handling code is error-handling code
  • Do not use exception handling for normal processing
  • Since they automatically deallocate, RAII is a key technique to use for all variables

Why RAII is Important

Partial std::exception Hierarchy

std::logic_error

faulty logic within the program such as violating logical preconditions or class invariants and may be preventable

  • std::invalid_argument argument value is not accepted
  • std::domain_error inputs are outside of the domain defined for the operation
  • std::length_error exceed implementation-defined length limits for some object
  • std::out_of_range access elements out of defined range

std::runtime_error

errors due to events beyond the scope of the program and where the program code cannot easily predict the error

  • std::range_error the destination type cannot represent the result of a computation
  • std::overflow_error arithmetic overflow errors, i.e., the result of a computation is too large for the destination type
  • std::underflow_errorthe result of a computation is a subnormal floating-point value

catch Handlers

  • Parameter modifiers const and volatile are ignored
  • catch (...) matches any type
  • catch (Foo& e) matches any object of type Foo or any object of any class derived from Foo
  • Order matters: Put derived class catch handlers before base class catch handlers

Catch Order

Goal: One try per Function

Recommendations

  • Throw objects
    • It is possible to throw any type, including int and char*, but more difficult to determine what the error is
  • Throw objects derived from std::exception
    • That way, catch handlers for std::exception will catch your throw
  • Catch by reference, i.e., &
    • const is thrown away, not really concerned about advantages of const at this point
    • Can also catch by pointer, but pointers cause the question: “Who should free?

Constructors

throw;

Exception Dispatcher: Re-Throw Idiom

noexcept specifier

  • Current:
    • non-throwing - void f() noexcept;
    • potentially throwing - void f();
  • Deprecated:
    • non-throwing - void f() throw();
    • potentially throwing - void f() throw(std::exception);

Other Issues

  • C++ has no “finally”, use RAII instead
  • Exception handling may or may not be slow, but shouldn’t cause a slowdown of normal processing
  • If a framework throws pointers, use pointers (e.g., MFC)

Overall Design

  • Inherit from the standard exceptions, e.g., std::exception and family
  • In general, one try block per function. Good idea to extract function if more than one is needed
  • Organize around the logical reason for the exception, not the source (e.g., subsystem)
    • E.g., Banking app, Reason: “insufficient funds”, Subsystem: One of many
  • Design for the entire system
    • Avoid a layer-by-layer design

Scenario: Starting Code

Scenario: Adding Exception Handling

Problem

Scenario: Summation

  • In C++, the destructor cannot be called for a partially-constructed object
  • When a constructor throws an exception, the destructor of the class cannot cleanup fields/data members
  • Every data member should have RAII semantics, and not rely on class destructor