Cast during unwinding - why does this example work?

Consider this:

void thrower () { throw "123"; } struct Catcher { ~ Catcher () { try {thrower ();} catch (...) {} } }; int main () { try { Catcher c; throw 1.23; } catch (...) {} } 

This compiles and runs without calling terminate in gcc 4.3, but according to the standard (15.5.1)

... when the exception handling mechanism, after completing the evaluation of the expressed expression, but before the exception is caught (15.1), calls the user function, which exits through the uncaught exception ... completion must be called.

When the ~Catcher call is called after the double has been reset, it is "after the evaluation is complete ... before the exception is caught", and thrower is the "user-defined function that leaves the uncaught exception", this satisfies the above condition. Yes, char* caught, but only after the user function exits.

Should I call terminate ?

To emphasize this:

 void do_throw () { throw "123"; } void thrower () { do_throw (); // Uncaught exception here (A) } struct Catcher { ~ Catcher () { try {thrower (); /* (B) */} catch (...) {} } }; int main () { try { Catcher c; throw 1.23; } catch (...) {} } 

(A) occurs in context (B), which already has an exception.

So should terminate not be called? If not, and this is a legal situation in which we can have two simultaneous exceptions, where do we draw the line?

+4
source share
5 answers

Various accents:

when the mechanism for handling exceptions , after completing the evaluation of the expressed expression, but before the exception is caught (15.1), calls the user function , which terminates via an uncaught exception ... terminate should be called

thrower() is a user function that exits an exception but is not called by the exception handling mechanism; it is called by another user-defined function ( Catcher destructor), which itself is called by the exception handling mechanism, and this function does not exit the exception.

+3
source

Why was the terminate call called? You have a catch(...) block that catches all exceptions: one that catches double (one in main ) and one that catches char const[4] (one that is in ~Catcher ).

Thus, no functions "exit the uncaught exception" because all exceptions are caught.

The keywords here are: "calls a user-defined function that ends with an uncaught exception." This is not the same as "calling a user-defined function, which somewhere along the line calls a user-defined function, which ends with an uncaught exception." If you call a function and it has a try/catch , and then you call some function that exits with an exception, but you encounter an exception, terminate not called.

tl; dr: the very first call in the call hierarchy that occurs between evaluating exceptions and before catch must exit with an exception for terminate to call, not branches below the first call. This makes sense because you cannot configure another catch between when the object was thrown and when it was caught.

+4
source

Which function caused by the exception handling mechanism exits through an uncaught exception. The only function called by the exception handling mechanism is Catcher::~Catcher() , and it normally exits reaching the end of the function.

And basically there is no limit to the number of simultaneously active exceptions. Limit - the number of exceptions that must propagate above a certain point. If Catcher::~Catcher() threw through an exception, we would have two exceptions to propagate up, one that caused the call to the destructor, and one that the destructor went through. As long as the destructor catches all the exceptions and does not throw them, there are no problems.

+3
source

... when the exception handling mechanism, after completing the evaluation of the expression to be thrown, but before the exception is caught (15.1), calls the user function, which leaves the uncaught exception ... the completion must be called.

The exception handling mechanism calls ~Catcher() , which does not exit through an uncaught exception; therefore it does not call std::terminate . It does not call (directly) thrower() .

+3
source

This is a perfectly legal code. You will catch everything that is thrown before it causes problems. Only if you remove try / catch in ~ Catcher will you throw an exception while unwinding.

0
source

All Articles