Search

14.4 — Uncaught exceptions and catch-all handlers

By now, you should have a reasonable idea of how exceptions work. In this lesson, we’ll cover a few more interesting exception cases.

Uncaught exceptions

In the past few examples, there are quite a few cases where a function assumes its caller (or another function somewhere up the call stack) will handle the exception. In the following example, mySqrt() assumes someone will handle the exception that it throws -- but what happens if nobody actually does?

Here’s our square root program again, minus the try block in main():

Now, let’s say the user enters -4, and mySqrt(-4) raises an exception. Function mySqrt() doesn’t handle the exception, so the program stack unwinds and control returns to main(). But there’s no exception handler here either, so main() terminates. At this point, we just terminated our application!

When main() terminates with an unhandled exception, the operating system will generally notify you that an unhandled exception error has occurred. How it does this depends on the operating system, but possibilities include printing an error message, popping up an error dialog, or simply crashing. Some OSes are less graceful than others. Generally this is something you want to avoid altogether!

Catch-all handlers

And now we find ourselves in a conundrum: functions can potentially throw exceptions of any data type, and if an exception is not caught, it will propagate to the top of your program and cause it to terminate. Since it’s possible to call functions without knowing how they are even implemented (and thus, what type of exceptions they may throw), how can we possibly prevent this from happening?

Fortunately, C++ provides us with a mechanism to catch all types of exceptions. This is known as a catch-all handler. A catch-all handler works just like a normal catch block, except that instead of using a specific type to catch, it uses the ellipses operator (…) as the type to catch.

If you recall from lesson 7.14 on ellipses and why to avoid them, ellipses were previously used to pass arguments of any type to a function. In this context, they represent exceptions of any data type. Here’s an simple example:

Because there is no specific exception handler for type int, the catch-all handler catches this exception. This example produces the following result:

We caught an exception of an undetermined type

The catch-all handler should be placed last in the catch block chain. This is to ensure that exceptions can be caught by exception handlers tailored to specific data types if those handlers exist. Visual Studio enforces this constraint -- I am unsure if other compilers do. (Per reader Lonami in the comments below, GCC does too).

Often, the catch-all handler block is left empty:

This will catch any unanticipated exceptions and prevent them from stack unwinding to the top of your program, but does no specific error handling.

Using the catch-all handler to wrap main()

One interesting use for the catch-all handler is to wrap the contents of main():

In this case, if runGame() or any of the functions it calls throws an exception that is not caught, that exception will unwind up the stack and eventually get caught by this catch-all handler. This will prevent main() from terminating, and gives us a chance to print an error of our choosing and then save the user’s state before exiting. This can be useful to catch and handle problems that may be unanticipated.


14.5 -- Exceptions, classes, and inheritance
Index
14.3 -- Exceptions, functions, and stack unwinding

41 comments to 14.4 — Uncaught exceptions and catch-all handlers

  • koe

    Reposting in case it got lost by the reply system.

    Hi Alex (and nascardriver),

    I recently got to the end of chapter 15, which is considered the end of the core content on this website. In an attempt to 'give back' for all your wonderful work, I researched and wrote up a draft lesson for noexcept. While it may not be 100% up to the standards of the other lessons here (especially given my lack of experience with C++), I hope my legwork is enough to get it added after your revisions.

    Since noexcept was originally developed in response to the advent of move semantics, I feel this new lesson would be best placed as section 15.8 (a and b, since it's rather long).

    If you are interested in getting a copy of the lesson, please email me at the email associated with this comment (if you have access to it, otherwise we can work something out). The text is too long to paste here.

    Thank you!

    P.S. A preview of the section headers:
    //start: 15.8a noexcept and std::move_if_noexcept
    ::The strong guarantee and move constructors
    ::std::move_if_noexcept
    ::noexcept
    ::The noexcept operator

    //15.8b noexcept: compared to throw(), when to use it
    ::noexcept vs dynamic exception specifier throw
    ::When to use noexcept
    ::Situation 1: no-throw by circumstance
    ::Situation 2: no-throw as a guarantee

    • nascardriver

      Hi koe, thanks for your efforts already!

      I saw your other comment, I'm waiting for Alex to get in touch with you. I suppose he's busy or on vacation or something.
      Don't worry, I won't forget about you :)

    • Alex

      I reached out via email. Nascardriver was right, I was on vacation. :)

  • Mohammed

    Why do I return 1 in the last code from the main function? Shouldn't it be return 0?

    • nascardriver

      The `return 1` was supposed to be in the catch block. I updated the lesson. Thanks for pointing it out!
      EDIT: nevermind, it was correct is written. `runGame` is an infinite loop. `main` only returns if an error occurs.

      • koe

        Is there some way of ending a program other than terminating main()? E.g. what would a call to 'exitGame()' do?

        • nascardriver

          Yes there are (`std::exit`, `std::terminate`), but they'll prevent resources from being cleaned up, so don't use them unless absolutely necessary.

  • Abdelrahman

    Hello,
    Thanks for this content it is really helpful.
    I have a question when using ellipsis to catch all exceptions, is there any possible way to use the va_list, va_start, va_arg, va_end stuff to infer the type of the exception thrown or not ?

    • nascardriver

      No, you need to know the type of the exception beforehand. You can rethrow the exception to another handler that tries all kinds of types that may be thrown (See example at https://en.cppreference.com/w/cpp/error/current_exception#Example ), but you can't get the type automatically.

  • Hassan Muhammad

    Hi Alex,
       I've been one year off and had some revision afterwards, guess what, everything is now back on track(thumbs up for your hardwork).
       By the way, i think i've a firm grasp of this chapter(up to this point though), but is there a way to identify the kind(say data type) of the exception caught by "catch-all block"?

    • nascardriver

      Hi!

      If you don't know the type of something, you can't do anything with it. Exceptions are thrown at run-time, where types no longer exist (for the most part), so you can't determine the type there.
      If an exception is a derivative of `std::exception`, you can catch it as such.
      Otherwise, you can use `std::current_exception` to forward the unknown exception to a function where you use `std::rethrow_exception` and try to catch all possible types. There is no way to catch by `auto` or similar.

  • David

    Do you have a source for Bjarne Stroustrup considering exception specifiers a failed experiment? I'd be interested in reading that. I read his book "The Design and Evolution of C++" and in the chapter on exceptions he briefly mentions exception specifiers as an idea for making exceptions part of the interface of a function so callers could know what exceptions to expect, but I don't believe he mentioned any dislike for this idea or even if it made it into C++ (apparently it did!)

    • Alex

      I can't find the citation I used when I wrote the article, so I've removed the quote. The article doesn't rely on that for justification anyway. The fact that dynamic exception specifiers have been removed from the language as of C++17 is justification enough of them being a failed experiment!

  • Asgar

    Hi Alex,

    About exception specifiers, isn't there a 4th type, in addition to the 3 mentioned?

    So,
    1. throw()
    2. throw() with a particular data type
    3. throw(...)
    and then
    4. noexcept

    You discourage the use of the first 3. Isn't the 4th one used quite a lot, as of C++11?

    • Alex

      Yes, noexcept was added in C++11 and this lesson hadn't been updated since then. I've added a short blurb about it to the lesson, and added it to my to do list to come back and do the topic justice.

      • koe

        Hi Alex (and nascardriver),

        I recently got to the end of chapter 15, which is considered the end of the core content on this website. In an attempt to 'give back' for all your wonderful work, I researched and wrote up a draft lesson for noexcept. While it may not be 100% up to the standards of the other lessons here (especially given my lack of experience with C++), I hope my legwork is enough to get it added after your revisions.

        Since noexcept was originally developed in response to the advent of move semantics, I feel this new lesson would be best placed as section 15.8 (a and b, since it's rather long).

        If you are interested in getting a copy of the lesson, please email me at the email associated with this comment (if you have access to it, otherwise we can work something out). The text is too long to paste here.

        Thank you!

        P.S. A preview of the section headers:
        //start: 15.8a noexcept and std::move_if_noexcept
        ::The strong guarantee and move constructors
        ::std::move_if_noexcept
        ::noexcept
        ::The noexcept operator

        //15.8b noexcept: compared to throw(), when to use it
        ::noexcept vs dynamic exception specifier throw
        ::When to use noexcept
        ::Situation 1: no-throw by circumstance
        ::Situation 2: no-throw as a guarantee

  • Trevor29

    Hi Alex
    Is there any way of saving the exception value for later use in the catch all exception catch(...)? I am thinking of code that would print an error message and perhaps do some tidying up, then throw the exception again for the calling code to deal with (or not!).
    It seems to me that leaving exceptions unhandled for higher level code to handle could be a good way of generating memory leaks if calls to destructors are bypassed.
    Thanks
    Trevor

    PS: I have read ahead to Section 14.6 which covers rethrowing exceptions. Does "throw" without a parameter work in this case?

    • Alex

      Yes, throw without a parameter is what you'd want to use in this case. Using this, you can have an exception handler do some cleanup and then "pass the buck" up the call stack for someone else to deal with.

      Exceptions are a one good reason to avoid doing your own local memory management within a function -- if you hit an exception (or early return) somewhere between the allocation and deallocation, the deallocation will never happen. There are ways around this though, namely smart pointers (covered in chapter 15), which use RAII principles to ensure destruction happens even if exceptions or early returns or other things happen between the allocation and deallocation...

  • Rev

    Just a comment about fact of exception specifiers. I by chance opened a library file of DevC++ and saw the creators added throw() to the constructors of the auto_ptr class. So what you said that not many programmers knew about it is not quite true :)

  • Peter Baum

    I'm actually back at the end of chapter 6 and just finished my blackjack program.  I ended up here because I needed exception handling for error checking purposes.  In particular, gracefully catching conversion errors from stoi() and similar situations.  It seems to me that the sections on exception handling are placed way too late in the lesson sequence.  By the time a student reaches the end of chapter 6, they should be programming relatively sophisticated programs, and the basics of exception handling should certainly be taught before objects. You know... IMHO.

    • Alex

      It's always a struggle to determine the best point to introduce topics.

      Many programmers don't use exceptions at all (the Google C++ style guide advises Google employees not to use them). So part of the calculus here is that it's one of C++'s more optional features, and thus should occur later in the tutorial, even if it might be useful.

      Nevertheless, I appreciate the feedback.

  • pingsoli

    Exception sepcifiers 'throw()' is deprecated, using noexcept will be more elegant. :)

    By the way, I really like your post. Thanks a lot.

  • Hardik

    The above segment gives me an error("expected type-specifier before '...' token.")
    Should I conclude that it isn't supported by my compiler?

  • Ninja

    I kind of forgot the reasons for this (if it was explained in an earlier lesson) but why do you end statements with std::endl when they start with std::cerr but use '\n' to end statements starting with std::cout? I always use '\n' for everything. It doesn't matter right?

    • Alex

      std::cerr is unbuffered, so it's always flushed immediately. Using std::endl (which causes both a newline and a flush) is a bit extraneous, in that the flush isn't needed. But since std::cerr should (hopefully) be called only sporadically, we don't need to worry about performance. So it ultimately boils down to a stylistic choice. std::endl reminds me that it will always be output immediately.

  • Vlad

    Could we use the error handling as a switch ()? Would it be more efficient, or is this heresy? Something like this:

    This won't compile, though: expected primary expression before 'catch', but it's just an example.

    • Vlad

      Besides that I just wrote this ad-hoc (void instead of double), I realized I forgot to add try{}. It works, but I still wonder if it can be used in real life?

      • Alex

        I think you could. Exceptions were designed as a way to pass errors from a function up the stack, so a higher-level function can handle the error. Passing a negative value to a sqrt function is definitely an error, and having the caller handle the error in some way that makes sense does seem reasonable.

    • Alex

      You could I suppose, though I've never seen this in practice. Exceptions are inherently not all that performant, so you'd definitely be better off using an actual switch.

  • "Visual Studio enforces this constraint -- I am unsure if other compilers do."

    GCC does too! Can be disallowed with -fpermissive though

  • Alice

    Hi Alex,

    I have two questions.

    (1) In the first example, you do not have the return value for main. I also tried it and it did not give a compiling error. I wonder why. Shouldn't there be a return value since we specify int main()?

    (2) Is there anyway to tell the value from thrown statement instead of data type? For example, can I throw all int values and in the corresponding catch statement, we do a if-else or switch-case kind of thing to work on the values from thrown. Because I was thinking we will run out the use of data type as the amount is limited.

    Thanks!

    • Alex

      1) Unintentional omission. I've fixed the example. Visual Studio is permissive about not having a return statement at the end of main, but it really shouldn't be.
      2) Yes, you can access the value through the variable in the catch statement, and switch based on that:

  • DOBRESCU Mihai

    Hi,

    I love your sense of humour.


    // Look ma, no exception handler!
    cout << "The sqrt of " << dX << " is " << MySqrt(dX) << endl;

  • octavsly

    I wonder why throwing with an anonymous variable can be caught with a reference to the variable...

    try
    {
    throw 5.0 ;
    }
    catch (double &dX)
    {
    cout << "We caught an exception of type double: " << dX << endl;
    }

    Probably throw creates a variable... It is just strange to see.

    • Alex

      Yup. When you throw an exception, the throw expression creates a temporary object, called an exception object, that is then passed to the appropriate catch block. Unlike other temporary objects, the exception object is treated as an lvalue, and thus can be caught by non-const reference.

  • vidya

    Hi,

    How to catch exceptions occurred in DLL.
    For example
    main()
    {
    calling the "A.DLL" (DLL(donot know the source code) is generating unhandled exception, access violation)
    }

    How I do catch it in my main program so that I can terminate it properly

    Regards,
    Vidyakumar

    • Alex

      My understanding is that throwing exceptions across DLL boundaries is generally a bad idea.

      It only works if the DLL is compiled using exactly the same C++ compiler version as your code, and you need to ensure the same runtime library is used for both client and DLL.

      Generally, it's better to use some other method of returning errors across DLL boundaries, like error codes.

  • Peter

    Hi there,

    I'm just learning c/c++. I am from a Java background and very used to a method specifying the Exceptions it is throwing. How would you know what Exceptions to expect from a function in c++ then? Hope that the function is well documented? Otherwise wouldn't you always have to check for the general exception?!

    Thank you, Peter

    • Kiena

      As an analogy, you can think of c++ exceptions as java runtime exceptions or errors.

      In java, errors and runtime exceptions can optionally be specified in the signature as well, but handling them is never mandatory. In c++, by specifying them, you could enforce exception checking, but even compilers may not support this, according to the tutorial. Many developers consider checked exceptions a mistake in the java language too (probably because of their misuse even in the standard api).

Leave a Comment

Put all code inside code tags: [code]your code here[/code]