Search

14.6 — Rethrowing exceptions

Occasionally you may run into a case where you want to catch an exception, but not want to (or have the ability to) fully handle it at the point where you catch it. This is common when you want to log an error, but pass the issue along to the caller to actually handle.

When a function can use a return code, this is simple. Consider the following example:

In the above code snippet, the function is tasked with creating a Database object, opening the database, and returning the Database object. In the case where something goes wrong (e.g. the wrong filename is passed in), the exception handler logs an error, and then reasonably returns a null pointer.

Now consider the following function:

In the case where this function succeeds, it returns an integer value -- any integer value could be a valid value.

But what about the case where something goes wrong with getIntValue()? In that case, getIntValue() will throw an integer exception, which will be caught by the catch block in getIntValueFromDatabase(), which will log the error. But then how do we tell the caller of getIntValueFromDatabase() that something went wrong? Unlike the top example, there isn’t a good return code we can use here (because any integer return value could be a valid one).

Throwing a new exception

One obvious solution is to throw a new exception.

In the example above, the program catches the int exception from getIntValue(), logs the error, and then throws a new exception with char value ‘q’. Although it may seem weird to throw an exception from a catch block, this is allowed. Remember, only exceptions thrown within a try block are eligible to be caught. This means that an exception thrown within a catch block will not be caught by the catch block it’s in. Instead, it will be propagated up the stack to the caller.

The exception thrown from the catch block can be an exception of any type -- it doesn’t need to be the same type as the exception that was just caught.

Rethrowing an exception (the wrong way)

Another option is to rethrow the same exception. One way to do this is as follows:

Although this works, this method has a couple of downsides. First, this doesn’t throw the exact same exception as the one that is caught -- rather, it throws a copy-initialized copy of variable exception. Although the compiler is free to elide the copy, it may not, so this could be less performant.

But significantly, consider what happens in the following case:

In this case, getIntValue() throws a Derived object, but the catch block is catching a Base reference. This is fine, as we know we can have a Base reference to a Derived object. However, when we throw exception, the thrown exception is copy-initialized from variable exception. Variable exception has type Base, so the copy-initialized exception also has type Base (not Derived!). In other words, our Derived() object has been sliced!

You can see this in the following program:

This prints:

Caught Base b, which is actually a Derived
Caught Base b, which is actually a Base

The fact that the second line indicates that Base is actually a Base rather than a Derived proves that the Derived object was sliced.

Rethrowing an exception (the right way)

Fortunately, C++ provides a way to rethrow the exact same exception as the one that was just caught. To do so, simply use the throw keyword from within the catch block (with no associated variable), like so:

This prints:

Caught Base b, which is actually a Derived
Caught Base b, which is actually a Derived

This throw keyword that doesn’t appear to throw anything in particular actually re-throws the exact same exception that was just caught. No copies are made, meaning we don’t have to worry about performance killing copies or slicing.

If rethrowing an exception is required, this method should be preferred over the alternatives.

Rule: When rethrowing the same exception, use the throw keyword by itself.

14.7 -- Function try blocks
Index
14.5 -- Exceptions, classes, and inheritance

23 comments to 14.6 — Rethrowing exceptions

  • Ritesh

    Hey Alex,
    All the exception classes are derived from the std::exception, so if we rethrow an exception like this given below

    I tried doing so and I get the same result like before with just throw which should not slice except object. But both print SOME ERROR  
    Why aren't the exception classes getting sliced.
    They are also derived from the exception.

    • Alex

      The string "SOME ERROR" is being stored in the std::exception part of the class, so it can be displayed even though the class has been sliced.

  • Akshay

    Alex,
    In the first example, consider this snippet:

        try
        {
            Database *d = new Database(filename);
            d.open(); // assume this throws an int exception on failure
            return d;
        }

    Since d is a pointer to a database object, shouldn't the second line be d->open()? Or am I missing something?

  • David

    Typo: There is a BBCode code tag on line 1 of the last example, and another just below the "This prints" portion of that example.

  • Lamont Peterson

    Alex,

    This example program produces this output for me:

    [code]Caught Base b, which is actually a Base
    Caught Base b, which is actually a Base[/code/

    My code is the same as yours, except that I did '\n' instead of "\n" and maybe for some whitespace, neither of which would explain a discrepancy.  But, looking at this code, I would expect the output I got.  I'm not sure why catching a Base object in the nested try-catch block should result in b being a Derived in the first place (that didn't happen in any previous examples, either).

    Am I missing something?

    • Lamont Peterson

      Alex,

      I did a little more experimenting just now; here's the code I whipped up:

      The compile command:

      The resulting output I got:

      This all indicates to me that the object slicing happens when "catch (Base& b)" creates the object "b", but that by using "throw;" vs. "throw b;", we are re-trhowing the "input" to the catch block, not the object that input created (for use within the catch block) and the "throw b;" is not re-trhow'ing, but rather, it throws the object b.  This makes a lot more sense to me.

      Any thoughts?  Am I missing something else?

      • Alex

        I believe the slicing happens at the point of throwing, as this is where the copy of the object being thrown is made. Using "throw" throws the whole object, whereas "throw b" throws whatever type b is -- if b is a Base reference, then b will throw a Base, even if b was pointing to a Derived object.

        • Lamont Peterson

          I'm not sure that fits the evidence.  If the slicing happens at the "throw b;" statement (e.g., if "throw something;" creates an object instance), then the subsequent re-trhow with the "throw;" statement later in the chain should be re-throwing the already sliced object.  Since that's not what we're seeing here, it makes me think that re-trhowing in the "proper" way (e.g., "throw;") must be throwing the object from the input to the catch () statement, not the result of the catch () statement.

          Perhaps stating it another way:  with the simple "throw;", there's nothing to give the compiler an idea of what to slice.  With "throw b;" it's throwing whatever "b" is, yet, I see no hint to the compiler to slice something.  The "throw" statement, when being used to re-throw, wouldn't slice because it's not creating an object, merely to throw an existing one.  Only the very first throw (e.g., "throw Derived ();"), which is using a constructor, would be creating an object.

          Therefore, what I see is that catch statements for base classes operating upon a thrown object of a derived class, must be where the slicing happens.  At least, as evidenced by my example.  If we modify it to put "catch (Base &b)" before "catch (Derived &d)", then we'll never catch Derived objects, because the Base catch will get to it first.

          You mentioned that throwing creates a copy of the object and actually throws the copy (which seems oddly inefficient to me).  I'd have to think through ti more, but on the face of it, I'm not sure if actually happens or even if it really does, does it matter?  Perhaps more testing is needed (or reading through compiler source code, but I think testing would be faster and source for some compilers isn't readily available)?

          • Alex

            > Perhaps stating it another way: with the simple "throw;", there’s nothing to give the compiler an idea of what to slice.

            Yes, this. Is there some way I can make that more clear?

            In your example where catch(Base &b) is first, that catch can match either a Base object or an object derived from Base, so if it's first, it'll gobble up any exceptions that match. This doesn't imply anything about where the slicing happens.

            I can't remember the precise reason why exceptions throw a copy of the object -- I think it's because the thing being thrown could go out of scope when the block is exited. You should be able to see the copy constructor being called. But that said, the copy can be elided, or the compiler might opt to do a move (in C++11) instead, depending on circumstances. If you do a search on stack overflow, I'm sure you can find more discussion about why copies are thrown rather than references.

    • Alex

      When you throw an object, a copy of that object is made, and the type of that copy is equivalent to whatever type the object being thrown is. Thus, if you throw a Derived object, a copy of the Derived object is made. So when you catch a Base reference, the Base reference will be to the Derived object that was thrown. Virtual function resolution should still work at this point.

      However, if you then throw the Base reference, a copy of the Base portion (only) is made, and this is where the trouble begins, as we've now sliced the object.

      • Lamont Peterson

        Alex,

        OK, and I agree. However, the problem is still present; the code example produces different output than what the lesson suggests, and I think the output I got was correct.  Then I "played around with it" a bit more and produced an example which I think more fully illustrates the subtlety of this (and I think both you and I said the same thing about that code in the other comment).

        So, I'm suggesting that your code example and the output needs a little updating.  Feel free to use / modify the example I gave.

        • Alex

          I might be being dense here, but I'm not seeing the difference between my results and yours. Where do you think your code example produces a result that the lesson does not suggest?

          Edit: I've also updated the example for the "throw" case so the results are more directly comparable.

          • Lamont Peterson

            Alex,

            In the lesson, you state that the output should be:

            Caught Base b, which is actually a Derived
            Caught Base b, which is actually a Base

            But the output I get is:

            Caught Base b, which is actually a Base
            Caught Base b, which is actually a Base

            • Alex

              This might be an error in your compiler. I've tried this with several compilers now (Visual Studio 2017, GCC 4.6.3 and 6.3) and gotten the result suggested in the lesson.

              • Lamont Peterson

                That would explain the difference I'm seeing.  That thought definitely warrants more tests.

                But, I'm still hung up on the logic making more sense to me the way it's behaving here than if it behaved the way your lesson says I should see.

                Thanks for the discussion (and all of them).  Interesting stuff and I've been learning good things from the comments.  Keep up the good work 🙂 !

  • Loveya

    Hi Alex, to prevent object slicing why don't we throw references instead?

  • Nice to see that you are still updating the website....

    Nice article btw

Leave a Comment

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