Nested Exceptions

Learn about nested exceptions in C++: from basic concepts to advanced handling techniques
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access
3D Character Concept Art
Ryan McCombe
Ryan McCombe
Posted

Whilst handling an exception within a catch block, there may be scenarios where yet another exception can occur, thus leaving us with two exceptions being active at the same time.

To handle this, we have the concept of a nested exception. This allows us to throw a new exception, and for that exception to include a reference to the original. So, the original exception is said to be nested within the new exception.

Creating Nested Exceptions

To do this, we use the std::throw_with_nested() function, passing in our new exception:

// Auth.h
#pragma once
#include <string>

class AuthenticationError{/*...*/} class SecurityError : public std::exception {}; void Auth( std::string Email, std::string Password) { throw AuthenticationError{Email, Password}; } void Login( std::string Email, std::string Password) { try { Auth(Email, Password); } catch (AuthenticationError& e) { std::throw_with_nested(SecurityError{}); } }

Above, our Login() function will emit a SecurityError, with an AuthenticationError nested within it. We can catch the SecurityError in the normal way:

#include <iostream>
#include "Auth.h"

int main() {
  try {
    Login("test@example.com", "secret");
  } catch (SecurityError& e) {
    std::cout << "Caught a SecurityError";
  }
  std::cout << "\nProgram recovered";
}

When we handle the exception in this way, we also handle any nested exceptions that it might have contained. As a result, our program fully recovers:

Caught a SecurityError
Program recovered

Typically, however, this is not what we want. If we’re using nested exceptions in our project, we need to consider the possibility that the exceptions we receive might contain nested exceptions that also need to be handled. We cover techniques on how to do this in the next few sections.

Accessing Nested Exceptions

The throw_with_nested() function implements nested exceptions using multiple inheritance, meaning a class can inherit from multiple base classes. This is a technique we’ll cover in detail later in the course.

For now, we can think of the object created by throw_with_nested() as having two types. One of the types is std::nested_exception, and the other is the type of object we provided as an argument. In the previous example, that was our user-defined type SecurityError:

std::throw_with_nested(SecurityError{});

To check if our type is a std::nested_exception, we can dynamic_cast it in the usual way:

#include <iostream>
#include "Auth.h"

int main() {
  try {
    Login("test@example.com", "secret");
  } catch (SecurityError& e) {
    std::cout << "Caught a SecurityError";

    auto NestedException{
      dynamic_cast<std::nested_exception*>(&e)};
    if (NestedException) {
      std::cout <<
        "\nThere's a nested exception in here";
    }
  }
  std::cout << "\nProgram recovered";
}
Caught a SecurityError
There's a nested exception in here
Program recovered

However, the easiest way to detect and deal with nested exceptions tends to be to rethrow them.

Rethrowing Nested Exceptions

Once we’ve dealt with the outer exception, we can decide to rethrow the nested exception, if there is one.

The easiest way to do this is through the std::rethrow_if_nested() function:

#include <iostream>
#include "Auth.h"

int main() {
  try {
    Login("test@example.com", "secret");
  } catch (SecurityError& e) {
    std::cout << "Caught a SecurityError";

    try {
      std::rethrow_if_nested(e);
    } catch (...) {
      std::cout << "\nThere's a nested "
        "exception in here";
    }
  }
  std::cout << "\nProgram recovered";
}
Caught a SecurityError
There's a nested exception in here
Program recovered

As we might expect, we can handle a nested exception like any other. Our catch block can specify the exception type it is capable of handling:

#include <iostream>
#include "Auth.h"

int main() {
  try {
    Login("test@example.com", "secret");
  } catch (SecurityError& e) {
    std::cout << "Caught a SecurityError";

    try {
      std::rethrow_if_nested(e);
    } catch (AuthenticationError& e) {
      std::cout
        << "\nNested AuthenticationError: "
        << e.Email;
    }
  }
  std::cout << "\nProgram recovered";
}
Caught a SecurityError
Nested AuthenticationError: test@example.com
Program recovered

Also, we don’t need to handle nested exceptions locally. We can just throw them and let a function elsewhere on the call stack deal with it:

void SomeFunction() {
  try {
    Login("test@example.com", "secret");
  } catch (SecurityError& e) {
    std::cout << "Caught a SecurityError";
    std::rethrow_if_nested(e);
  }
}

Handling Nested Exceptions Recursively

Exceptions can be nested arbitrarily deeply. For example, in a complex project that uses nested exceptions, we can have an exception, nested within an exception, nested within another exception.

The contrived GenerateExceptions() function simulates this scenario:

#include <iostream>

void GenerateExceptions() {
  try {
    throw std::runtime_error{"Error One"};
  } catch (...) {
    try {
      std::throw_with_nested(
        std::runtime_error{"Error Two"});
    } catch (...) {
      std::throw_with_nested(
        std::runtime_error{"Error Three"});
    }
  }
}

When we catch an exception, we’ll often want to unwind all of the exceptions contained within it. In the following example, the HandleExceptions() function demonstrates how we can do this:

#include <iostream>

void GenerateExceptions() {/*...*/} void HandleExceptions(const std::exception& e) { std::cout << "\nHandling " << e.what(); try { std::rethrow_if_nested(e); } catch (const std::exception& Nested) { HandleExceptions(Nested); // Recursion } } int main() { try { GenerateExceptions(); } catch (std::exception& e) { HandleExceptions(e); } }
Handling Error Three
Handling Error Two
Handling Error One

This is an example of a recursive function. A recursive function conditionally calls itself.

In this case, HandleException() calls itself if std::rethrow_if_nested(e); throws an exception. In other words, it calls itself if the exception it received as a parameter contains a nested exception.

Every invocation of HandleException() handles one of those nested exceptions, until there are none left, at which point the recursion stops.

Recursion is a relatively unintuitive concept, so don’t worry if this examples doesn’t make sense. We cover recursion starting with simpler examples in a dedicated lesson later in the course.

Summary

This lesson provided an exploration of nested exceptions, demonstrating their implementation, handling, and practical applications. The key topics we learned included:

  • Understanding of nested exceptions and their usage with std::throw_with_nested() for handling multiple layers of exceptions.
  • Familiarity with accessing and identifying nested exceptions using dynamic_cast and std::nested_exception.
  • Knowledge of rethrowing nested exceptions using std::rethrow_if_nested() and handling them appropriately.
  • Insight into recursively handling deeply nested exceptions, showcasing how to systematically unravel and address each layer of errors.

Was this lesson useful?

Edit History

  • First Published

Ryan McCombe
Ryan McCombe
Posted
Lesson Contents

Nested Exceptions

Learn about nested exceptions in C++: from basic concepts to advanced handling techniques

7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access
Exceptions and Error Handling
7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 114 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Function Try Blocks

Learn about Function Try Blocks, and their importance in managing exceptions in constructors
3D Character Concept Art
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved