The University of Iowa
The College of Liberal Arts and Sciences
Department of Computer Science

Programming Languages and Tools:

CS:3210:0001

Lecture/Lab #18

Programming with C++

Error handling/exceptions, RAII

Warm up

  • How do we check if reading from a stream was successful?

  • How do we reset the state of the input stream?

Error handling options

  • Throwing an exception
  • Returning a value indicating failure
  • Terminating the program
int read_with_error_checking(std::istream& in) {
    int n;
    if ( in >> n ) // check for errors
        return n;

    std::cout << "Error reading from stream\n";
    return -1;
}

int main() {
    std::cout << "Enter an integer: ";
    int n = read_with_error_checking( std::cin );
    if ( n != -1 )
        std::cout << "You entered: " << n << "\n";
}

Throwing an exception

int read_with_error_checking(std::istream& in) {
    int n;
    if ( in >> n ) // check for errors
        return n;

    throw std::runtime_error("Error reading from stream");
}
  • Exceptions are a control flow mechanism designed for more scalable handling of run-time errors
  • "Throwing" an exception terminates the regular control flow and transfers the execution to a matching `catch` handler in the most recently entered try/catch statement
  • Exiting of functions and destruction of the corresponding local variables in the process of handling an exception is called "stack unwinding"

Throwing an exception (cont.)

  • If there is no matching try/catch block, the program is terminated
  • It's recommended to catch exceptions by const reference
  • Derived exceptions will match a base class catch clause
  • The order of class catch clauses matters: If multiple matching catch handlers are present, the first matching handler is selected.
  • You can rethrow the old or throw a new exception from the catch block.

When to throw an exception

  • Rare errors / errors that should not be ignored (e.g. out of memory)
  • An error that cannot be handled by an immediate caller (e.g. network outage)
  • No suitable return path for errors codes are available (e.g. constructors).
  • The code flow is made more complicated or expensive by a need to pass both a value and an error indicator back.
  • When in doubt, prefer exceptions.

When to return an error code

  • A failure is normal and expected (e.g. opening a file).
  • An immediate caller can reasonably be expected to handle the failure.

Consider using std::optional if choosing this route.

When to terminate

  • Unrecoverable errors (e.g. memory exhaustion)

"Resource acquisition is initialization"

In plain language, RAII simply means tying resource management to the object lifetime:

  • Allocate/obtain resource in object ctor
  • Deallocation/release resource in object dtor

... where a resource could be almost anything that needs to be explicitly managed: memory, file handles, process handles, network sockets, database connections, mutexes, locks, etc.

Made with Slides.com