Reputation: 11181
I know that throwing from a destructor is in general a bad idea, but I was wondering if i could use std::uncaught_exception()
to safely throw from a destructor.
Consider the following RAII type:
struct RAIIType {
...
~RAIIType() {
//do stuff..
if (SomethingBadHappened()) {
//Assume that if an exception is already active, we don't really need to detect this error
if (!std::uncaught_exception()) {
throw std::runtime_error("Data corrupted");
}
}
}
};
Is this UB in c++11? Is it a bad design?
Upvotes: 7
Views: 752
Reputation: 136256
I know that throwing from a destructor is in general a bad idea, but I was wondering if i could use
std::uncaught_exception()
to safely throw from a destructor.
You may like to have a look at uncaught_exceptions proposal from Herb Sutter:
Motivation
std::uncaught_exception
is known to be “nearly useful” in many situations, such as when implementing an Alexandrescu-style ScopeGuard. [1] In particular, when called in a destructor, what C++ programmers often expect and what is basically true is: “uncaught_exception returns true iff this destructor is being called during stack unwinding.”However, as documented at least since 1998 in Guru of the Week #47, it means code that is transitively called from a destructor that could itself be invoked during stack unwinding cannot correctly detect whether it itself is actually being called as part of unwinding. Once you’re in unwinding of any exception, to uncaught_exception everything looks like unwinding, even if there is more than one active exception.
...
This paper proposes a new function int
std::uncaught_exceptions()
that returns the number of exceptions currently active, meaning thrown or rethrown but not yet handled.A type that wants to know whether its destructor is being run to unwind this object can query uncaught_exceptions in its constructor and store the result, then query uncaught_exceptions again in its destructor; if the result is different, then this destructor is being invoked as part of stack unwinding due to a new exception that was thrown later than the object’s construction.
Upvotes: 1
Reputation: 5127
Note that your code doesn't do what you think it does. In case SomethingBadHappened
and there is no stack unwinding in place, you attempt to throw from a destructor and nonetheless std::terminate
is called. This is the new behavior in C++11 (see this article). You will need to annotate your destructor with noexcept(false)
specification.
Suppose you do this, it is not clear what you mean by "safely". Your destructor never triggers std::terminate
directly. But calling std::terminate
is not a UB: it is very well defined and useful (see this article).
For sure, you cannot put your class RAIIType
into STL containers. The C++ Standard explicitly calls that UB (when a destructor throws in an STL container).
Also, the design look suspicious: the if-statement really means "sometimes report a failure and sometimes not". Are you fine with this?
See also this post for a similar discussion.
Upvotes: 2
Reputation: 254461
It depends what you mean by "safely".
That will prevent one of the issues with throwing from a destructor - the program won't be terminated if the error happens during stack unwinding when handling another exception.
However, there are still issues, among them:
Upvotes: 0
Reputation: 66922
You have an if
, did you think about the "other" condition? It can throw an exception or... do what? There's two things that can be in the other branch.
Now that we've established that there's no purpose to throwing an exception conditionally like that, the rest of the question is sort of moot. But here's a tidbit: NEVER THROW EXCEPTIONS FROM DESTRUCTORS. If an object throws an exception, the calling code normally checks that object in some way to "handle" the exception. If that object no longer exists, there's usually no way to "handle" the exception, meaning the exception should not be thrown. Either it's ignored, or the program makes a dump file and aborts. So throwing exceptions from destructors is pointless anyway, because catching it is pointless. With this is mind, classes assume that destructors won't throw, and virtually every class leaks resources if a destructor throws. So NEVER THROW EXCEPTIONS FROM DESTRUCTORS.
Upvotes: 3