sbabbi
sbabbi

Reputation: 11181

Is this a safe way of throwing an exception from a destructor?

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

Answers (4)

Maxim Egorushkin
Maxim Egorushkin

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

Andrzej
Andrzej

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

Mike Seymour
Mike Seymour

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:

  • If you have an array of these, then they may not all be destroyed if one throws on destruction.
  • Some exception-safety idioms rely on non-throwing destruction.
  • Many people (such as myself) don't know all the rules governing what will or won't be correctly destroyed if a destructor throws, and won't be confident that they can use your class safely.

Upvotes: 0

Mooing Duck
Mooing Duck

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.

  • Nothing (If nothing needs to happen when the error occurs, why throw an exception?)
  • It "handles" the exception (If it can be "handled", why throw an exception?)

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

Related Questions