Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use std::nested_exception and friends?

I've noticed that there are a few more interesting declarations in <exception> in C++11. Can anybody shed any light on what they mean and how to use them?

The ones I'm wondering about are:

  1. ::std::nested_exception
  2. ::std::throw_with_nested
  3. ::std::rethrow_if_nested

Additionally, while they seem self-explanatory, it might be nice to know how these worked:

  1. ::std::exception_ptr
  2. ::std::make_exception_ptr
  3. ::std::current_exception
  4. ::std::rethrow_exception
like image 420
Omnifarious Avatar asked Dec 06 '11 08:12

Omnifarious


1 Answers

Some high level code will generically just catch std::exception and print the what(). You want to squeeze as much information as possible to this generic mechanism, yet without losing any information. Consider an implementation of some archive library:

archive::archive(const char* filename)
{
    ifstream file(filename);
    file.exceptions(ios_base::badbit);
    open_archive(file); // throws ios_base::failure, or some other low-level exception.
}

The information available to the archive is not recorded (e.g. filename). Besides you would like to distinguish exceptions that came from the archive class from other exceptions.

archive::archive(const char* filename)
{
    try {
        ifstream file(filename);
        file.exceptions(ios_base::badbit);
        open_archive(file); // throws ios_base::failure, or some other low-level exception.
    } catch(const std::exception& e) {
        throw archive_exception("Can't open archive", filename, e.what());
    }
}

Now we added higher-level semantic information that archive class knows, but we also lost the information about the original cause of problem (the type of e). nested_exception is meant to solve this problem:

archive::archive(const char* filename)
{
    try {
        ifstream file(filename);
        file.exceptions(ios_base::badbit);
        open_archive(file); // throws ios_base::failure, or some other low-level exception.
    } catch(...) {
        throw_with_nested(archive_exception("Can't open archive", filename));
    }
}

All the available information is recorded. We can now generically retrieve it in the catch site:

void print_exception_info(const std::exception& e)
{
    cerr << e.what() << "\n";
    try {
        rethrow_if_nested(e);
    } catch(const std::exception& ne) {
        print_exception_info(ne);
    } catch(...) { }
}

int main() {
    try {
        run();
    } catch(const std::exception& e) {
        print_exception_info(e);
    }
}

The output will be more descriptive than before. It will describe the problem starting from the high-level to the low-level:

Can't open archive "my_archive.bin"

Access is denied.

Or perhaps:

Can't open archive "my_archive.bin"

Record 'aabb' not found.

The functions working with exception_ptr are designed to transfer exceptions between threads, or more generally, store an exception for later use. How they work depends on the implementation. The intention was that exception_ptr will be a shared pointer to the exception object. However when this pointer is created, when throwing the exception or when trying to get an exception_ptr to it, is subject to the implementation. The implementation is still free to copy the exception when you call current_exception().

like image 61
Yakov Galka Avatar answered Oct 13 '22 20:10

Yakov Galka