cplusplus.co.il

Catching uncaught exceptions within terminate

Posted on: 21/03/2010

The handler std::terminate() is called whenever the exception handling mechanism cannot find a suitable catch clause for a thrown exception (and in some other cases. For example, when an exception is thrown during the handling of another exception – see this GotW post about std::uncaught_exception). It is possible to define a custom handler by using std::set_terminate.

In this post we would like to create a terminate handler which will be able to catch the exception that led to its invocation, when there is one.

Consider the following main function:

#include <stdexcept>

int main () {
    throw std::runtime_error("error!");
}

Now, let us create our own terminate handler, which we will call ‘our_terminate’. We will use a trick to configure it even before the main function gets to run, by invoking set_handler as part of a global constant initialization (be aware of the random initialization order of global objects):

#include <exception>

void our_terminate (void); // decleration

namespace {
    static const bool SET_TERMINATE =
        std::set_terminate(our_terminate);
}

At this point, we are left with the challenge of writing the actual terminate handler we’re after. As far as I know there is no portable solution. The solution we will present here works for GCC (verified on versions 3.* and 4.*). It does not seem to work on Microsoft’s Visual Studio (2008), so a different approach should be taken there. It is always possible to check where the compiler places the currently active exception, and just take it from there (thus providing a compiler specific workaround for every compiler). Feel free to comment on the matter.

Normally, an empty throw directive (‘throw;’) outside of a catch clause (‘throw;’ within a catch clause means a re-throw of the caught exception) would lead to the invocation of the terminate handler. The interesting thing however, is that under GCC, using such an empty throw statement within our custom terminate handler will cause a re-throw of the currently active exception (if there is one), making the following code behave as desired:

#include <iostream>

void our_terminate (void) { // try 1
    try { throw; }
    catch (const std::runtime_error &err) {
        std::cout << "caught a runtime_error. what(): " <<
            err.what() << std::endl;
    }
    catch (...) {
        std::cout << "unknown exception occured." <<
            std::endl;
    }
}

That actually works, for the reasons specified above. But this solution is far from being complete; What if an empty throw directive led to the invocation of our custom terminate handler?

int main () {
    throw;
}

There is no active exception, so the empty throw statement is actually executed in an endless loop (by repeatedly re-invoking the terminate handler, until the stack is exhausted).

So we would like to invoke ‘throw;’ only once. A possible solution is this:

void our_terminate (void) { // try 2
    static bool tried_throw = false;

    try {
        if (!tried_throw++) throw;
        std::cout << "no active exception" << std::endl;
    }
    catch (const std::runtime_error &err) {
        std::cout << "caught a runtime_error. what(): " <<
            err.what() << std::endl;
    }
    catch (...) {
        std::cout << "unknown exception occured." <<
            std::endl;
    }
}

The empty throw will not be attempted the second time around, making this solution work as expected – even for the case where there is no pending exception.

In case the terminate handler is likely to be invoked more than once in your own environment, I would recommend resetting the ‘tried_throw’ flag back to false after printing the proper message.

Advertisements

2 Responses to "Catching uncaught exceptions within terminate"

Yes, It doesn’t work in Visual Studio to my sorrow, actually I have had this problem before you posted this article, but I must admit that this is something that should be built in the language, because I was trying to build a logger that will be decleared in each function and while destructed will print the exception description neatly.

makes me want to drink alchoholic beverages

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: