7

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?

sbabbi
  • 11,070
  • 2
  • 29
  • 57

4 Answers4

4

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.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
2

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.

Andrzej
  • 5,027
  • 27
  • 36
1

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.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Funny thing is, I learnt about `uncaught_exceptions` recently, but I forgot that I posted this question. – sbabbi Sep 30 '15 at 10:51
  • `uncaught_exceptions()` isn't merely a *proposal*, but `bool std::uncaught_exceptions()` part of the C++ standard, see also my answer given earlier. – Walter Sep 30 '15 at 11:46
  • @Walter You seem to be confusing things. `std::uncaught_exceptions` is to be available in C++17, see http://en.cppreference.com/w/cpp/error/uncaught_exception. gcc-5.2, for example, does not provide it. – Maxim Egorushkin Sep 30 '15 at 11:53
  • @MaximEgorushkin Ahem. Sorry, but it's **you who are confusing things**. As the very resource you're referring to says, `int std::uncaught_exceptions()` will be available in C++17, but **`bool std::uncaught_exceptions()`** (as mentioned in my previous comment) is available in C++11 (and will be deprecated in C++17, see also [here](http://www.cplusplus.com/reference/exception/uncaught_exceptio)). That website doesn't say this well, but a hint is given in the exceptions section. And code using this compiles fine with gcc 5.1. – Walter Sep 30 '15 at 12:35
  • @Walter _`bool std::uncaught_exceptions()` is available in C++11_ - nope, it is not. You may like to learn how to read carefully and thoughtfully. – Maxim Egorushkin Sep 30 '15 at 12:37
  • @MaximEgorushkin check [this](http://www.cplusplus.com/reference/exception/uncaught_exception) (it's actually around since C++98) and/or try it out (`#include ` with gcc 5.2). -- try again, mistyped the link. See also [this](http://stackoverflow.com/questions/27741423/why-will-stduncaught-exception-change-to-stduncaught-exceptions). – Walter Sep 30 '15 at 12:40
  • 1
    @Walter Dude, you keep confusing `uncaught_exception` with `uncaught_exceptions`. These are different things. – Maxim Egorushkin Sep 30 '15 at 12:46
  • A trailing `s` can cause a lot of confusion... a lot of this confusion can be avoided by specifying the, `uncaught_exceptions` returns an `int` not a `bool`. – sbabbi Sep 30 '15 at 17:50
0

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.
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644