Why doesn't C++ use std::nested_exception to allow throwing from destructor?
The problem you cite happens when your destructor is being executed as part of the stack unwinding process (when your object was not created as part of stack unwinding)1, and your destructor needs to emit an exception.
So how does that work? You have two exceptions in play. Exception X
is the one that's causing the stack to unwind. Exception Y
is the one that the destructor wants to throw. nested_exception
can only hold one of them.
So maybe you have exception Y
contain a nested_exception
(or maybe just an exception_ptr
). So... how do you deal with that at the catch
site?
If you catch Y
, and it happens to have some embedded X
, how do you get it? Remember: exception_ptr
is type-erased; aside from passing it around, the only thing you can do with it is rethrow it. So should people be doing this:
catch(Y &e)
{
if(e.has_nested())
{
try
{
e.rethrow_nested();
}
catch(X &e2)
{
}
}
}
I don't see a lot of people doing that. Especially since there would be an exceedingly large number of possible X
-es.
1: Please do not use std::uncaught_exception() == true
to detect this case. It is extremely flawed.
Why I couldn't handle an exception thrown from a destructor outside of it?
I think you misunderstood the recommendation:
a destructor usually should not throw an exception like STL containers and if it should, that thrown expression should be wrapped in a try-catch block (a catch must handle that exception).
In other words, it says:
You should not throw an exception from a destructor.
Now the question arises what one should do if the destructor is:
~Bar() {
do_something();
}
and do_something()
might throw an exception. In this case the recommendation is to catch the exception and handle it:
~Bar() {
try {
do_something();
} catch(...) {
// handle exception, eg write a log message
// but do not retrow it or a different one!
}
}
Now there is no way the exception leaves the destructor.
As you discovered, catching the exception outside of the destructor does not help to solve the general problem, because here:
try{
Foo f{};
Foo* pFoo = new Foo{};
delete pFoo; // normally exception caught here nad handled by the following catch
}
The call to delete pFoo;
throws an exception. During stack unwinding Foo f{};
will be destroyed and another exception will be thrown from the destructor. If an exception is thrown during stack-unwinding std::terminate
will be called. And the way to avoid that to happen is to not throw exceptions from destructors.
Using RAII to nest exceptions
In the absence of a method to catch (and consume) the uncaught exception in the destructor, there is no way to rethrow an exception, nested or not, in the context of the destructor without std::terminate
being called (when the exception is thrown in the context of exception handling).
std::current_exception
(combined with std::rethrow_exception
) will only return a pointer to a currently handled exception. This precludes its use from this scenario as the exception in this case is explicitly unhandled.
Given the above, the only answer to give is from an aesthetic perspective. Function level try blocks make this look slightly less ugly. (adjust for your style preference):
void foo() try {
// code that might throw
std::ifstream file("nonexistent.file");
file.exceptions(std::ios_base::failbit);
}
catch(...) {
std::throw_with_nested(std::runtime_error("foo failed"));
}
libstdc++ std::throw_with_nested() requires polymorphic types
N3337 [except.nested]/5,6
[[noreturn]] template <class T> void throw_with_nested(T&& t);
Let
U
beremove_reference<T>::type
.
5. Requires:U
shall be
CopyConstructible
.
6. Throws: ifU
is a non-union class type not
derived fromnested_exception
, an exception of un-specified type that is publicly derived from bothU
andnested_exception
and constructed fromstd::forward<T>(t)
, otherwisestd::forward<T>(t)
.
Also consider that nested_exception
is polymorphic as it has a virtual destructor ([except.nested]/2):
[ Note:
nested_exception
has a virtual destructor to make it a
polymorphic class. Its presence can be tested for withdynamic_cast
.
— end note ]
The exception-type that is actually thrown is always polymorphic. U
doesn't have to be, though - just CopyConstructible
, as noted by the Requires-section.
So libstdc++ has an invalid implementation. It should internally specialize for types for which is_base_of<nested_exception, U>::value
is false
.
c++ exception : throwing std::string
Yes. std::exception
is the base exception class in the C++ standard library. You may want to avoid using strings as exception classes because they themselves can throw an exception during use. If that happens, then where will you be?
boost has an excellent document on good style for exceptions and error handling. It's worth a read.
Should exceptions be chained in C++?
It is necessary to copy the data out of an exception object, into a chain, if you want it to outlive the catch
block that receives it, aside from rethrow by throw;
. (Which includes, for example, if that catch
block exits through a throw obj;
.)
This can be done by putting data to be saved on the heap, and implementing swap
(move
in C++0x) on your private data inside the exception, for example.
Of course, you need to be careful when using the heap with exceptions… but then again, in most modern OSes, memory overcommitment completely prevents new
from ever throwing, for better or for worse. A good memory margin and dropping exceptions from the chain upon complete meltdown should keep it safe.
struct exception_data { // abstract base class; may contain anything
virtual ~exception_data() {}
};
struct chained_exception : std::exception {
chained_exception( std::string const &s, exception_data *d = NULL )
: data(d), descr(s) {
try {
link = new chained_exception;
throw;
} catch ( chained_exception &prev ) {
swap( *link, prev );
} // catch std::bad_alloc somehow...
}
friend void swap( chained_exception &lhs, chained_exception &rhs ) {
std::swap( lhs.link, rhs.link );
std::swap( lhs.data, rhs.data );
swap( lhs.descr, rhs.descr );
}
virtual char const *what() const throw() { return descr.c_str(); }
virtual ~chained_exception() throw() {
if ( link && link->link ) delete link; // do not delete terminator
delete data;
}
chained_exception *link; // always on heap
exception_data *data; // always on heap
std::string descr; // keeps data on heap
private:
chained_exception() : link(), data() {}
friend int main();
};
void f() {
try {
throw chained_exception( "humbug!" );
} catch ( std::exception & ) {
try {
throw chained_exception( "bah" );
} catch ( chained_exception &e ) {
chained_exception *ep = &e;
for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
std::cerr << ep->what() << std::endl;
}
}
}
try {
throw chained_exception( "meh!" );
} catch ( chained_exception &e ) {
for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
std::cerr << ep->what() << std::endl;
}
}
}
int main() try {
throw chained_exception(); // create dummy end-of-chain
} catch( chained_exception & ) {
// body of main goes here
f();
}
output (appropriately grumpy):
bah
humbug!
meh!
RAII way to get errors that are caught during destruction
You might partially handle the case of failure in destructor:
class Foo {
public:
Foo() : count(std::uncaught_exceptions()) {}
~Foo() noexcept(false)
{
if (std::uncaught_exceptions() != count) {
// ~Foo() called during stack unwinding
// Cannot throw exception safely.
} else {
// ~Foo() called normally
// Can throw exception
}
}
private:
int count;
};
How to throw a C++ exception
Simple:
#include <stdexcept>
int compare( int a, int b ) {
if ( a < 0 || b < 0 ) {
throw std::invalid_argument( "received negative value" );
}
}
The Standard Library comes with a nice collection of built-in exception objects you can throw. Keep in mind that you should always throw by value and catch by reference:
try {
compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
// do stuff with exception...
}
You can have multiple catch() statements after each try, so you can handle different exception types separately if you want.
You can also re-throw exceptions:
catch( const std::invalid_argument& e ) {
// do something
// let someone higher up the call stack handle it if they want
throw;
}
And to catch exceptions regardless of type:
catch( ... ) { };
I want to catch an exception and bundle it within my own exception and throw upwards
How about a nested exception?
try { /* ... */ }
catch (...)
{
throw MyException("An error occurred", std::current_exception());
}
Just make a suitable class that stores the exception:
struct MyException : std::exception
{
std::string message;
std::exception_ptr nested_exception;
MyException(std::string m, std::exception_ptr e)
: message(std::move(m))
, nested_exception(std::move(e))
{ }
// ...
};
When the exception is caught, the catcher can rethrow the nested exception:
try { /* load resource */ }
catch (MyException & e)
{
log("Resource loading failed: " + e.what());
std::rethrow_exception(e.nested_exception);
}
In fact, this entire logic is already provided by the standard library via std::throw_with_nested
.
Related Topics
32-Bit to 16-Bit Floating Point Conversion
Cannot Find or Open the Pdb File in Visual Studio C++ 2010
Static_Assert Fails Compilation Even Though Template Function Is Called Nowhere
Foreach Macro on Macros Arguments
What Exactly Do "Ib" and "Ub" Mean
C++ - Why Is Boost::Hash_Combine the Best Way to Combine Hash-Values
Pointers, Smart Pointers or Shared Pointers
What Does "Default" Mean After a Class' Function Declaration
C++ Static Member Initialization (Template Fun Inside)
Why Doesn't C++ Use Std::Nested_Exception to Allow Throwing from Destructor
Calculating Normals in a Triangle Mesh
C++ How to Determine Whether a Pointer Points to a Valid Object
How to Include Openssl in Visual Studio
How to Get the Argument Types of a Function Pointer in a Variadic Template Class
Overload Resolution Between Object, Rvalue Reference, Const Reference