Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exceptions inside exceptions in C++

Tags:

c++

exception

When working with C# or Java, I used to create exception classes that included other exceptions as class members. For instance:

public class MyException : Exception {
    private MyException ex;
    private String message;

    public String what() {
        return this.message;
    }

    public String stack() {
        if(ex != null) {
            StringBuilder s;
            s.append(this.what());
            s.append(this.ex.stack());
            return s.toString();
        }

        return this.what();
    }
}

I was looking for examples on the same subject but for C++ and I couldn't find any (maybe I didn't search for the right terms, as you can see the title of this question is not very fancy).

Anyway, what is the correct way of doing that in C++? Is it to store the inner exception as a pointer or a reference? (I think I might need a pointer so it can be null when it's the first exception). When I raise the exception, should it be as a pointer created with new?

EDIT: Maybe what I wrote was a little confusing or not a well-known (-accepted) programming practice. So I'll specify how I intended to use this class with a snippet:

 try {
     // Code that throws an exception of type MyException
 } catch(MyException ex) {
     MyException another = new MyException();
     another.setEx(ex);
     another.setMessage("A message that explains where the other exception was caught and on what conditions");

     throw another;
 }
like image 700
Renan Avatar asked Jul 05 '11 02:07

Renan


1 Answers

There is no correct way to do it with the standard exceptions in C++03 because they're designed to be used polymorphically but can't be cloned. So if you catch std::exception const& e you could store a copy but this will lead to slicing, losing all useful information. You should not store a pointer or reference to the exception because its lifetime will come to an end as soon as leaving the catch clause (assuming you don't rethrow the original exception).

You could get around this limitation if you knew each and every type that can be thrown and testing them all but that's not a nice design (i.e. it subverts polymorphism). It makes more sense to write a base exception class of your own that can be cloned, and catch that. You still have the problem if you catch an std::exception that came from someone else's code however.

At this point I feel compelled to mention Boost.Exception. It makes it easy to write your own exception hierarchy and provides various utilities, among them boost::exception_ptr. You can then do:

typedef boost::error_info<struct tag_nested_exception, boost::exception_ptr>
    nested_exception;

// ...
catch(...) {
    // better: use BOOST_THROW_EXCEPTION
    throw your_exception_type() << nested_exception( boost::current_exception() );
}

This is so useful that boost::diagnostic_info supports it and will display the nested exception for you (it's undocumented). It's even been suggested that this nested_exception typedef should be part of the library, too; in the meantime it's easy to write it yourself.

Don't expect magic though: boost::current_exception 'captures' the active exception (fine print: or a clone of it) fine only if the throw site used boost::enable_current_exception. (Functionally, this is the moral equivalent of using a base exception class that can be cloned). If it didn't, it won't fail but some information may be lost.


As a final note, know that the design of Boost.Exception was adopted for C++0x. Thus the following correctly stores the active exception, with none of the boost::current_exception caveats since it has language support:

// you can still use Boost.Exception:
typedef boost::error_info<struct tag_nested_exception, std::exception_ptr>
    nested_exception;

// ...
catch(...) {
    // e has type std::exception_ptr
    auto e = std::current_exception();
    // internally store the std::exception_ptr
    throw your_exception_type(e);

    // or with Boost
    BOOST_THROW_EXCEPTION( your_exception_type() << nested_exception(e) );
}

There is also a std::nested_exception type that can very easily be used like so:

catch(...) {
    // throws an unspecified type derived from your_exception_type
    // and std::nested_exception
    std::throw_with_nested(your_exception_type());
}
like image 51
Luc Danton Avatar answered Nov 10 '22 03:11

Luc Danton