Search

15.4 — Uncaught exceptions, catch-all handlers, and exception specifiers

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. 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 OS’s are less graceful than others. Generally this is something you want to avoid altogether!

Catch-all handlers

And now we find ourselves in a condundrum: 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, 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.

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.

Exception specifiers

This subsection should be considered optional reading because exception specifiers are rarely used in practice, are not well supported by compilers, and Bjarne Stroustrup (the creator of C++) considers them a failed experiment.

Exception specifiers are a mechanism that allows us to use a function declaration to specify whether a function may or will not throw exceptions. This can be useful in determining whether a function call needs to be put inside a try block or not.

There are three types of exception specifiers, all of which use what is called the throw (…) syntax.

First, we can use an empty throw statement to denote that a function does not throw any exceptions outside of itself:

Note that DoSomething() can still use exceptions as long as they are handled internally. Any function that is declared with throw() is supposed to cause the program to terminate immediately if it does try to throw an exception outside of itself, but implementation is spotty.

Second, we can use a specific throw statement to denote that a function may throw a particular type of exception:

Finally, we can use a catch-all throw statement to denote that a function may throw an unspecified type of exception:

Due to the incomplete compiler implementation, the fact that exception specifiers are more like statements of intent than guarantees, some incompatibility with template functions, and the fact that most C++ programmers are unaware of their existence, I recommend you do not bother using exception specifiers.

15.5 -- Exceptions, classes, and inheritance
Index
15.3 -- Exceptions, functions, and stack unwinding

14 comments to 15.4 — Uncaught exceptions, catch-all handlers, and exception specifiers

  • 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).

  • 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

  • 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.

  • DOBRESCU Mihai

    Hi,

    I love your sense of humour.


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

  • puppi

    dear alex,

    why it didn’t throw an exception in this example?

  • 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:

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

    GCC does too! Can be disallowed with -fpermissive though

  • 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.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter