28

Is there some way to catch exceptions which are otherwise unhandled (including those thrown outside the catch block)?

I'm not really concerned about all the normal cleanup stuff done with exceptions, just that I can catch it, write it to log/notify the user and exit the program, since the exceptions in these casese are generaly fatal, unrecoverable errors.

something like:

global_catch()
{
    MessageBox(NULL,L"Fatal Error", L"A fatal error has occured. Sorry for any inconvience", MB_ICONERROR);
    exit(-1);
}
global_catch(Exception *except)
{
    MessageBox(NULL,L"Fatal Error", except->ToString(), MB_ICONERROR);
    exit(-1);
}
mdb
  • 52,000
  • 11
  • 64
  • 62
Fire Lancer
  • 29,364
  • 31
  • 116
  • 182

8 Answers8

26

This can be used to catch unexpected exceptions.

catch (...)
{
    std::cout << "OMG! an unexpected exception has been caught" << std::endl;
}

Without a try catch block, I don't think you can catch exceptions, so structure your program so the exception thowing code is under the control of a try/catch.

Byron Hawkins
  • 2,536
  • 2
  • 24
  • 34
EvilTeach
  • 28,120
  • 21
  • 85
  • 141
  • The thing is I really dont want to put my entire app in one big "super-try" block...since performance is kinda critical... I know theres some way somewhere because for example visual studio can detect/catch/whatever such exceptiosn and offer to break and debug. – Fire Lancer Nov 09 '08 at 16:58
  • The super try block works. You pay the cost of setting it up once in the main. Once is not a performance issue. – EvilTeach Nov 09 '08 at 17:00
  • just once? I'm sure once inside a try block it maintained some sort of "trace" for cleaning up after a throw, making the cost based on the contents of the block? – Fire Lancer Nov 09 '08 at 17:05
  • 9
    performance is not an issue, only when an exception is actually thrown does the system have to think about what to do. Stack unwinding is something the app has to do anyway as objects go out of scope. Besides, try it - its so easy to put try/catch in main, see if there is a performance hit. – gbjbaanb Nov 09 '08 at 17:14
  • As the context is that he wants to report and abort, the one try catch block should work for him. – EvilTeach Nov 09 '08 at 18:17
  • 3
    Using exception has practically no cost in modern compiler, unless an exception is thrown. At which point it is just as expensive as using any other method for finding and reporting errors. – Martin York Nov 09 '08 at 20:12
  • @Fire Lancer: The cost is only incurred if you have an exception. At which point your execution speed is no longer critical as something has gone wrong. – Martin York Nov 09 '08 at 20:13
  • 6
    The global catch(...) in main still won't catch exceptions in ctors of globals. E.g. A global std::vector(size_t(-1)) will deplete memory before main() is called. – MSalters Nov 10 '08 at 14:01
  • 3
    @msalters a technique to get around that is to make your globals pointers, and allocate them in the main instead. – EvilTeach Jan 22 '09 at 16:41
  • @FireLancer: I don't know how VS handles it, but the code emitted for gcc is the same, whether it is within a try block or not, it is only the code path in case of an exception actually happening that costss omething – PlasmaHH Mar 08 '12 at 14:07
  • this obviously has to be done for each thread to capture `all unhandled C++ exceptions`, which isn't always easily doable. – Pavel P Jun 05 '18 at 22:06
  • catch(...) didn't catch Unhandled exceptions – Jaziri Rami Oct 15 '20 at 15:44
26

Check out std::set_terminate()

Edit: Here's a full-fledged example with exception matching:

#include <iostream>
#include <exception>
#include <stdexcept>

struct FooException: std::runtime_error {
    FooException(const std::string& what): std::runtime_error(what) {}
};

int main() {
    std::set_terminate([]() {
        try {
            std::rethrow_exception(std::current_exception());
        } catch (const FooException& e) {
            std::cerr << "Unhandled FooException: " << e.what() << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "Unhandled exception: " << e.what() << std::endl;
        } catch (...) {
            std::cerr << "Unhandled exception of unknown type" << std::endl;
        }

        std::abort();
    });

    throw FooException("Bad things have happened.");
    // throw std::runtime_error("Bad things have happened.");
    // throw 9001;
}
kralyk
  • 4,249
  • 1
  • 32
  • 34
  • 1
    Can you access the exception in `terminate_handler`? – Konstantin A. Magg Oct 02 '17 at 13:41
  • 1
    @KonstantinA.Magg I don't think so. Since the exception can be of _any_ type, there's really no way to access it... – kralyk Oct 02 '17 at 13:43
  • 4
    Yes you can using std::uncaugth_exception() & std::current_exception{) – Hassen Dhia Oct 30 '17 at 10:57
  • @HassenDhia, how, though? `uncaugth_exception()` is just a flag, and `current_exception()` returns an [exception_ptr](https://en.cppreference.com/w/cpp/error/exception_ptr) (assuming it works at all in the terminate handler), which I think you can't really do anything with at this stage, except for maybe converting it to `bool`. C++ doesn't seem to let you get into its content without a suitable catch block, as far as I can understand it. – Sz. Jan 17 '22 at 00:05
  • 1
    @Sz. You can use `try { std::rethrow_exception(the_pointer); } catch(const YourException& e) { ... }` – kralyk Jan 21 '22 at 20:24
  • @kralyk, nice. (A pity I already upvoted your old comment of "I don't think so. Since the exception can be of any type, there's really no way to access it...", that upvote should be transferred to this new one of yours... Never mind, copying it then. :) ) – Sz. Feb 02 '22 at 02:06
12

You can use SetUnhandledExceptionFilter on Windows, which will catch all unhandled SEH exceptions.

Generally this will be sufficient for all your problems as IIRC all the C++ exceptions are implemented as SEH.

gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
9

Without any catch block, you won't catch any exceptions. You can have a catch(...) block in your main() (and its equivalent in each additional thread). In this catch block you can recover the exception details and you can do something about them, like logging and exit.

However, there are also downside about a general catch(...) block: the system finds that the exception has been handled by you, so it does not give any more help. On Unix/Linux, this help would constitute creating a CORE file, which you could load into the debugger and see the original location of the unexcepted exception. If you are handling it with catch(...) this information would be already lost.

On Windows, there are no CORE files, so I would suggest to have the catch(...) block. From that block, you would typically call a function to resurrect the actual exception:

std::string ResurrectException()
   try {
       throw;
   } catch (const std::exception& e) {
       return e.what();
   } catch (your_custom_exception_type& e) {
       return e.ToString();
   } catch(...) {
       return "Ünknown exception!";
   }
}


int main() {
   try {
       // your code here
   } catch(...) {
       std::string message = ResurrectException();
       std::cerr << "Fatal exception: " << message << "\n";
   }
}
paavo256
  • 181
  • 3
  • 2
    Windows has .dmp files, which are roughly equivalent to core files, but they get uploaded to the Windows Error Reporting website (if the user clicks "send") instead of littering the user's hard drive. Also, if you have a just-in-time debugger configured, Windows will break into the debugger instead. – bk1e Nov 09 '08 at 18:46
  • 1
    the constructor of std::string could throw itself an exception. And so does printing to std:cerr. Then again you might be lucky 99% of the time. – rxantos Sep 07 '15 at 17:37
7

Update: This covers c++98 only.

From More Effective C++ by Meyers (pg 76), you could define a function that gets called when a function generates an exception that is not defined by its exception specification.

void convertUnexpected()
{
    // You could redefine the exception here into a known exception
    // throw UnexpectedException();

    // ... or I suppose you could log an error and exit.
}

In your application register the function:

std::set_unexpected( convertUnexpected );

Your function convertUnexpected() will get called if a function generates an exception that is not defined by its exception specification... which means this only works if you are using exception specifications. ;(

oz10
  • 153,307
  • 27
  • 93
  • 128
6

Provided that C++11 is available, this approach may be used (see example from: http://en.cppreference.com/w/cpp/error/rethrow_exception):

#include <iostream>
#include <exception>

void onterminate() {
  try {
    auto unknown = std::current_exception();
    if (unknown) {
      std::rethrow_exception(unknown);
    } else {
      std::cerr << "normal termination" << std::endl;
    }
  } catch (const std::exception& e) { // for proper `std::` exceptions
    std::cerr << "unexpected exception: " << e.what() << std::endl;
  } catch (...) { // last resort for things like `throw 1;`
    std::cerr << "unknown exception" << std::endl;
  }
}

int main () {
  std::set_terminate(onterminate); // set custom terminate handler
  // code which may throw...
  return 0;
}

This approach also allows you to customize console output for unhandled exceptions: to have something like this

unexpected exception: wrong input parameters
Aborted

instead of this:

terminate called after throwing an instance of 'std::logic_error'
  what():  wrong input parameters
Aborted
scrutari
  • 1,378
  • 2
  • 17
  • 33
  • Does not work for me on windows. Calling `std::uncaught_exception` returns `false` even though termination is caused by unhandled `throw std::runtime_error("h");` – Diligent Key Presser Aug 24 '18 at 04:18
5

This is what I always do in main()

int main()
{
    try
    {
        // Do Work
    }
    catch(std::exception const& e)
    {
         Log(e.what());
         // If you are feeling mad (not in main) you could rethrow! 
    }
    catch(...)
    {
         Log("UNKNOWN EXCEPTION");
         // If you are feeling mad (not in main) you could rethrow! 
    }
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 3
    That's a good way to go, but one should keep in mind, that this will not catch possible static init exceptions (I believe). – kralyk Oct 22 '12 at 12:38
  • 1
    @kralyk: There is no way to catch exceptions generated in constructors/destructors of static storage duration objects. In these case `std::terminate()` is called. – Martin York Oct 22 '12 at 15:22
  • 2
    I know... Technically, it might be possible using set_terminate() (see my answer), but since the static init order is implementation-defined, that's no guarantee either... – kralyk Oct 26 '12 at 17:23
1

Use catch (...) in all of your exception barriers (not just the main thread). I suggest that you always rethrow (...) and redirect standard output/error to the log file, as you can't do meaningful RTTI on (...). OTOH, compiler like GCC will output a fairly detailed description about the unhandled exception: the type, the value of what() etc.

ididak
  • 5,790
  • 1
  • 20
  • 21