28

Is it possible to make a destructor catch exceptions and then re-throw them?
If so, how would I do that, since there isn't a clear place for a try statement?

Basically, I want to ideally do:

CMyObject::~CMyObject()  
{
    catch(...)    // Catch without a try.  Possible?
    {
        LogSomeInfo();
        throw;  // re-throw the same exception
    }
    // Normal Destructor operations
}

Background
I have a large, complex application that is throwing an unhandled exception somewhere. I don't have easy access to main or the top level message-pump or anything similar, so there's no easy place to catch all unhandled exceptions.

I figure any unhandled exception has to pass through a bunch of destructors as the stack is unwound. So, I'm contemplating scattering a bunch of catch statements in destructors. Then at least I'd know what objects are in play when the exception is thrown. But I have no idea if this is possible, or advisable.

Uddhav P. Gautam
  • 7,362
  • 3
  • 47
  • 64
abelenky
  • 63,815
  • 23
  • 109
  • 159
  • 4
    You may be able to use a [function try block](http://www.drdobbs.com/introduction-to-function-try-blocks/184401297), but seriously, just don't do this. You should never write a destructor that throws. – BoBTFish Sep 23 '13 at 15:53
  • Sounds like you would rather want a breakpoint that fires when an exception is thrown? – JustSid Sep 23 '13 at 15:54
  • @BoBTFish: First, this is to aid in debugging only. It won't end up in "shipping" code. Secondly, my destructor will not throw... it will catch the exception momentarily that was thrown somewhere else, and re-throw it without modification. – abelenky Sep 23 '13 at 15:54
  • @JustSid: Except that this "heisen-bug" never repros in a debugger. – abelenky Sep 23 '13 at 15:55
  • possible duplicate of [throwing exceptions out of a destructor](http://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor) – Zac Howland Sep 23 '13 at 15:55
  • 2
    Oh, you want to catch the exception that is causing the stack to unwind in the first place? – BoBTFish Sep 23 '13 at 15:57
  • @BoBTFish: Correct. Some unknown object, somewhere, is throwing an exception that goes unhandled, and causes the app to crash. I want to catch that exception anywhere (destructors seems to be a good place), and log some information to narrow down the problem. – abelenky Sep 23 '13 at 16:02
  • 1
    Whoever voted to close as Dup clearly didn't read the question: I'm looking to CATCH in a destructor, not throw. – abelenky Sep 23 '13 at 16:04
  • 3
    @abelenky: Actually, you're not looking to catch, you're looking to perform some additional processing during unwinding of an exception. – Ben Voigt Sep 23 '13 at 16:18
  • @Ben Voight: I would certainly take that too. `catch` seemed the most direct route, although `catch` without `try` is impossible. `uncaught_exception()` as suggested by @DanieldFrey seems to be the best choice. If you have another recommendation, I'm all ears. – abelenky Sep 23 '13 at 16:23
  • 1
    @abelenky I edited my answer with some important improvements. – Daniel Frey Sep 23 '13 at 18:38
  • The only exceptions that can be caught within a destructor are exceptions thrown within that destructor, or by function it calls. And, in many cases (such as if the object is an element of a standard container) it is NECESSARY. However, if a destructor is invoked because an exception was thrown, the destructor cannot catch that exception. – Peter Feb 27 '18 at 10:24
  • Sadly there are valid concerns other than debug purpose and such problems cannot solved by the alternative manners in the answers: to convert the resource (say, a stateful allocator object which would die after the call to the destructor) *in* the exception object being thrown. It can be worked around by wrapping *all* call sites with try-blocks, but this is a violation of seperation of concerns. As a result, mostly resources bounded by the lifetime of the class object cannot be transferred into the exception object directly. (Though it needs copy on some ABIs anyway...) – FrankHB Jan 25 '23 at 11:46

3 Answers3

17

EDIT: You can use std::uncaught_exception to check if an exception is currently being thrown (i.e. if stack unwinding is in progress due to an exception). It is not possible to catch that exception or otherwise get access to it from your destructor. So if your logging doesn't need access to the exception itself, you can use

CMyObject::~CMyObject()  
{
  if(std::uncaught_exception()) {
    LogSomeInfo(); // No access to exception.
  }
  // Normal Destructor operations
}

Note that this question was asked in 2013, meanwhile std::uncaught_exception was replaced with std::uncaught_exceptions (notice the additional s at the end) which returns an int. For a rationale, see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf, so if you are using C++17, you should prefer the new version. The above paper also explains why the old std::uncaught_exception will not work as expected in some situations.


Another option might be std::set_terminate. This is useful if you want to have a method called when an exception is not caught and about to terminate the program. In the terminate handler, I usually print some information about the exception and a (demangled) backtrace of where it originates from to my log file before finally terminating the program. This is compiler and system specific, but a real helper as it saves a lot of time if you write server processes and often the log file is all you get from ops.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • 3
    But delete the middle 6 lines. You have an *uncaught* exception, so throwing will invoke `std::terminate`. Plus, `throw;` is not valid outside a catch block. – Ben Voigt Sep 23 '13 at 15:55
  • `uncaught_exception` looks promising. I'm investigating it, thanks. – abelenky Sep 23 '13 at 16:05
  • @BenVoigt OK, I improved the answer. – Daniel Frey Sep 23 '13 at 16:13
  • Is it necessary to do both checks, `uncaught_exception` **and** the conversion to `bool` of `eptr`? `current_exception` returns "a null `exception_ptr` object if no exception is being handled" [propagation]/8? – dyp Sep 23 '13 at 18:44
  • @DyP In theory it's not necessary, this is why I wrote `"...this should not happen..."`, but it's better to be safe than sorry. Especially if otherwise a critical situation would crash without writing information to the log-file and rendering the bug impossible to debug. – Daniel Frey Sep 23 '13 at 18:46
  • 2
    @DanielFrey `uncaught_exception` and `current_exception` are **never** true at the same moment. `uncaught_exception` switches to false at the beginning of catch clause, and `current_exception` is false before this moment. Look at [this answer](http://stackoverflow.com/a/28267655/200121) to my question. – peper0 Feb 01 '15 at 21:32
  • You code prints "Weird, this should not happen!". Look at the @peper0 comment above. – anton_rh Feb 24 '18 at 03:12
  • Never, ever use `uncaught_exception`. It is utterly broken, and [it has been deprecated in C++17](http://en.cppreference.com/w/cpp/error/uncaught_exception). Use [`uncaught_exceptions`](https://stackoverflow.com/questions/27741423/why-will-stduncaught-exception-change-to-stduncaught-exceptions) - notice the final 's'. – sbabbi Feb 26 '18 at 14:31
  • @DanielFrey Ok I did not realize that this question was asked 4 years ago, and I forgot how I even got here. But is very nice that you updated the answer. – sbabbi Feb 27 '18 at 22:31
8

You can use std::uncaught_exception() which returns true if and only if there is an exception being processed. It has been available since C++98, and is superseded by std::current_exception which returns a std::exception_ptr.

However you must be careful not to throw another exception in an unguarded context, otherwise std::terminate will be caught. Example:

X::~X() {
    if (std::uncaught_exception()) {
        try {
            LogSomeInfo();
            // and do something else...
        } catch(...) {}
    }
}
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • The `try..catch` doesn't really help you here, does it? You already have an exception up in the air, so any throw by `LogSomeInfo` will be a double-fault and trigger `terminate` right away. – ComicSansMS Feb 28 '18 at 08:58
  • 1
    @ComicSansMS: I can assure you [that it works](https://ideone.com/k00j5y). If you were to allow the exception to escape the try/catch (by rethrowing from `catch` for example), then it would terminate. The way I see it, `try` opens a new exception context. – Matthieu M. Feb 28 '18 at 10:20
  • You are right, you only get in trouble if the exception passes above a function that was directly invoked by the exception handling mechanism (ie. the destructor itself), but it is okay to have exceptions thrown and caught below that. Sorry, for the confusion. – ComicSansMS Feb 28 '18 at 11:48
  • @ComicSansMS: No worries :) – Matthieu M. Feb 28 '18 at 11:55
7

A destructor cannot catch the exception that is causing the destruction of the instance.

You can only know if there is any "active exception" (see uncaught_exception) during the destruction (or, in C++17, how many of them there are there with uncaught_exceptions) but it's possible that the exception(s) are indeed going to be handled after that.

Dealing with exceptions is very hard, much harder than someone may think at a first sight and the reason is that exception safety doesn't scale by composition. This in my opinion means that is basically impossible to have non trivial stateful subsystems with strong exception safety (in case of an exception being thrown nothing happened to the internal state). This was discovered long ago (see 1994 Tom Cargill's "Exception handling: A False Sense of Security") but apparently is still ignored by large part of the C++ community.

The only reasonable way to handle exceptions I can think to is to have subsystems with clear well defined interfaces with thick "walls" (no side effect happening inside may escape), and that can be re-initialized to a well known state from scratch if needed when something goes wrong. This not trivial but can be done correctly to a reasonable extent.

In all other cases the global state of the system when an exception is caught is indefinite at best at the point of catch and there are in my opinion few use cases in which you can do anything in such a condition except dying immediately as loudly as possible instead of taking further actions without indeed knowing what is going on (dead programs tell no lie). Even keeping on calling destructors is somewhat questionable in my opinion.

Or you may try to be as functional as possible, but that's not an easy path either (at least for my brain) and it's also moving far away from reality (most computers are mutable objects with many billions of bits of mutable state: you can pretend this is not the case and they're instead mathematical functions with no state and with predictable output dependent on input... but in my opinion you're just deluding yourself).

6502
  • 112,025
  • 15
  • 165
  • 265
  • finding if there is an `uncaught_exception` at the time of destruction is good enough for my debugging purposes. If there is an uncaught_exception, chances are very good that it is at least tangentially related to the object's destruction. – abelenky Sep 23 '13 at 16:43
  • @abelenky: actually I was wrong on how `uncaught_exception()` works, so you can actually detect if a destruction is happening because of stack unwinding. – 6502 Sep 23 '13 at 16:58
  • Good to know I'm not alone in my doubts about the value of exceptions to the code. Only that my solution is more radical: I just disable them (which brings me back in the world where all program flow can explicitly be seen and be reasoned about...). – cmaster - reinstate monica Feb 27 '18 at 22:37