Exceptions

Overview

Teaching: 45 min
Exercises: 10 min
Questions
  • How do we handle major errors in C++?

  • How do exceptions differ from python?

Objectives
  • Learn about throwing and catching exception in C++

Lesson Contents

Prerequisites

  • Knowledge of basic C++ functions

Exception overview

Like python, C++ handles errors using exceptions.

Throwing exceptions

If we take our simple temperature conversion example, lets throw an exception if the temperature is below absolute zero and therefore unphysical.

First, we need to include the stdexcept component of the standard library. This will allow us to use the runtime_error exception.

#include <iostream>
#include <stdexcept>

const double absolute_zero = -459.67; // in Fahrenheit

double convert_F_to_C(double temperature)
{
    if(temperature < absolute_zero)
    {
        throw std::runtime_error("Temperature must be above absolute zero!");
    }

    return (temperature - 32.0)*(5.0/9.0);
}

int main(void)
{
    double temperature = -999.0;

    temperature = convert_F_to_C(temperature);

    std::cout << "Temperature is " << temperature << std::endl;
    
    return 0;
}
terminate called after throwing an instance of 'std::runtime_error'
  what():  Temperature must be above absolute zero!
Aborted (core dumped)

When run, this code will terminate with an exception, signifying that something went wrong and the program could not continue.

Catching exceptions

Exceptions can also be caught and appropriate handling of the error can happen. Often this means that the error is logged appropriately, and the program may then continue if the error was not serious.

If an exception is not caught, the result is program termination, as in the above example.

Exceptions in the standard library (like runtime_error) have a .what() function that returns a string describing the error. In runtime_error, this is the string passed to it when it is thrown.

Lets catch the exception, and write some better output to the terminal

#include <iostream>
#include <stdexcept>

const double absolute_zero = -459.67; // in Fahrenheit

double convert_F_to_C(double temperature)
{
    if(temperature < absolute_zero)
    {   
        throw std::runtime_error("Temperature must be above absolute zero!");
    }   

    return (temperature - 32.0)*(5.0/9.0);
}

int main(void)
{
    double temperature = -999.0;

    try {
        temperature = convert_F_to_C(-999.0);
    }
    catch(std::exception & ex)
    {
        std::cout << "Caught an exception! Could not convert temperature = " << temperature << std::endl;
        std::cout << "Exception: " << ex.what() << std::endl;
        return 1
    }

    std::cout << "Temperature is " << temperature << std::endl;
        
    return 0;
}
Caught an exception! Could not convert temperature = -999
Exception: Temperature must be above absolute zero!
Temperature is -999

Now, the error printed to the terminal is a bit better for the end user.

When we catch an exception, we give it the type of exception to catch. Typically, all exceptions (including exceptions in the standard library) derive from std::exception, so catching std::exception will catch all exceptions. You can narrow this if needed to only catch specific exceptions, or use (...) to catch absolutely everything.

Typically, exceptions are caught by reference to avoid copying. You can also catch by const reference if you would like.

Also note that we return 1 inside the exception handler. This is a signal to the operating system or parent process that this process exited abnormally. That is, return 0 from main signifies a successful run of the process, while returning non-zero signifies an error.

Key Points

  • Exceptions stop execution and go back up the call stack until they are caught

  • Exceptions are the main way to handle major errors in C++