Does throw inside a catch ellipsis (...) rethrow the original error in C++?
Asked Answered
G

1

38

If in my code I have the following snippet:

try {
  doSomething();
} catch (...) {
  doSomethingElse();
  throw;
}

Will the throw rethrow the specific exception caught by the default ellipsis handler?

Gnni answered 19/3, 2010 at 1:22 Comment(0)
G
47

Yes. The exception is active until it's caught, where it becomes inactive. But it lives until the scope of the handler ends. From the standard, emphasis mine:

§15.1/4: The memory for the temporary copy of the exception being thrown is allocated in an unspecified way, except as noted in 3.7.4.1. The temporary persists as long as there is a handler being executed for that exception.

That is:

catch(...)
{ // <--

    /* ... */

} // <--

Between those arrows, you can re-throw the exception. Only when the handlers scope ends does the exception cease to exist.

In fact, in §15.1/6 the example given is nearly the same as your code:

try {
    // ...
}
catch (...) { // catch all exceptions
    // respond (partially) to exception <-- ! :D
    throw; //pass the exception to some
           // other handler
}

Keep in mind if you throw without an active exception, terminate will be called. This cannot be the case for you, being in a handler.


If doSomethingElse() throws and the exception has no corresponding handler, because the original exception is considered handled the new exception will replace it. (As if it had just thrown, begins stack unwinding, etc.)

That is:

void doSomethingElse(void)
{
    try
    {
        throw "this is fine";
    }
    catch(...)
    {
        // the previous exception dies, back to
        // using the original exception
    }

    try
    {
        // rethrow the exception that was
        // active when doSomethingElse was called
        throw; 
    }
    catch (...)
    {
        throw; // and let it go again
    }

    throw "this replaces the old exception";
    // this new one takes over, begins stack unwinding
    // leaves the catch's scope, old exception is done living,
    // and now back to normal exception stuff
}

try
{
    throw "original exception";
}
catch (...)
{
  doSomethingElse();
  throw; // this won't actually be reached,
         // the new exception has begun propagating
}

Of course if nothing throws, throw; will be reached and you'll throw your caught exception as expected.

Garrett answered 19/3, 2010 at 1:24 Comment(9)
What if doSomethingElse() throws something else in the meantime? Curious thing.Grimsley
"and the handler it was caught in ends" sounds confusing. It's not necessary for its close-brace to be reached, according to the paragraph you quoted.Mercantile
@jdk: terminate gets called; see the last line of the example, throw 2; where 2 is a new exception being thrown.Stand
@Potatoswatter: It could use some rewording. But the "handler execution" stops when the last statement in the scope has been executed. (The scope ends.)Garrett
"two exceptions active, terminate is called". Um, no. Throwing a different exception replaces the active exception. 15.1/4: "when the [handler] exits by any means other than throw; the temporary object is destroyed". In particular, exiting by throw assignment-expression is "other". You're thinking of throwing from a destructor during unwinding before the handler is executed, and behaviour for that is explicitly defined in 15.2/3.Duplessis
@GMan, @jdk: that didn't sound right, I couldn't find anything in the standard to support it, so I tried an experiment with GCC, and indeed, throwing an exception within a catch block does not cause terminate or any other special behavior. According to §15.3/8, "An exception is considered handled upon entry to a handler." The only special thing about the execution environment within the throw block is that the exception object is referenced by rethrow. (And the special cases that apply to constructors/destructors.)Mercantile
Example code: pastebin.com/GaftqFFr. Note that doSomethingElse throws (from a destructor, no less), but terminate is not called. At least, not on my implementation, and not according to my reading of the standard.Duplessis
@Steve: Thanks, you and @Mercantile are of course correct, I read wrong. I've fixed it up to my understanding. (You were spot on in thinking I was in stack-unwind mode.) @jdk @Mike, sorry for the misinformation.Garrett
throw "this replaces the old exception"; is never reached, because the function ends with throw; // and let it go again.Wink

© 2022 - 2024 — McMap. All rights reserved.