Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is code with try-catch-rethrow equivalent to code w/o try-catch?

Tags:

Under which circumstances are the following two codes not equivalent?

{   // some code, may throw and/or have side effects }  try {   // same code as above } catch(...) {   throw; } 

edit Just to clarify, I'm not interested in (i) deviations from above pattern (such as more code in the catch block) nor (ii) intended to invite patronising comments about the proper usage of try-catch blocks.

I'm looking for a qualified answer referring to the C++ standard. This question was prompted by a comment by Cheers and hth. - Alf to this answer of mine, stating w/o further explanation that above codes are not equivalent.


edit they are indeed different. stack un-winding will be done in the latter, but not necessarily in the former, depending on whether an exception handler (some catch block higher up the stack) is found at run time.

like image 919
Walter Avatar asked Nov 13 '15 09:11

Walter


People also ask

What is Rethrow in exception handling?

If a catch block cannot handle the particular exception it has caught, you can rethrow the exception. The rethrow expression ( throw without assignment_expression) causes the originally thrown object to be rethrown.

Can I use throws instead of try catch?

Q #2) Can we use throws, try and catch in a single method? Answer: No. You cannot throw the exception and also catch it in the same method. The exception that is declared using throws is to be handled in the calling method that calls the method that has thrown the exception.

Does try catch stop execution?

The “try… First, the code in try {...} is executed. If there were no errors, then catch (err) is ignored: the execution reaches the end of try and goes on, skipping catch . If an error occurs, then the try execution is stopped, and control flows to the beginning of catch (err) .

Should you Rethrow an exception?

Upon determining that a catch block cannot sufficiently handle an exception, the exception should be rethrown using an empty throw statement. Regardless of whether you're rethrowing the same exception or wrapping an exception, the general guideline is to avoid exception reporting or logging lower in the call stack.


2 Answers

The latter mandates stack unwinding, whereas in the former it is implementation-defined if the stack is unwound.

Relevant standards quotes (all from N3337):

[except.ctor]/1: As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction.

[except.ctor]/3: The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” [...]

[except.terminate]/2: [When the exception handling mechanism cannot find a handler for a throw exception], std::terminate() is called (18.8.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called. [...]

As such, if you want to guarantee that your automatic objects have their destructors run in the case of an unhandled exception (e.g. some persistent storage must be mutated on destruction) then try {/*code*/} catch (...) {throw;} will do that, but {/*code*/} will not.

like image 144
TartanLlama Avatar answered Oct 22 '22 05:10

TartanLlama


Elaboratring on Cheers and hth. - Alf's comment:

From http://en.cppreference.com/w/cpp/error/terminate :

std::terminate() is called by the C++ runtime when exception handling fails for any of the following reasons:

1) an exception is thrown and not caught (it is implementation-defined whether any stack unwinding is done in this case)

So stack unwinding might not happen if your

{   // some code, may throw and/or have side effects } 

is not within another try/catch block.

Example:

struct A {     A() {}     ~A() { std::cout << "~A()" << std::endl; } };  int main() { //    try {         A a;         throw 1; //    } catch(...) { //        throw; //    } } 

Under coliru's gcc 5.2.0 with -O2 does not print ~A(), while with try/catch does print.

UPD: regarding your edit about separate compilation units, just tested with my local gcc 4.8.2, the behavior is the same: no stack unwinding if there is no catch. The particular example:

a.h:

struct A {    A();    ~A(); };  void foo(); 

a.cpp:

#include <iostream> using namespace std;  struct A {    A() {}    ~A() { cout << "~A()" << endl; } };  void foo() {     A a;     throw 1; } 

main.cpp:

#include "a.h"  int main () {    //try {     foo();    //} catch(...) {    //  throw;    //} } 

I think that whether there is a catch is determined at run time, because anyway when exception is thrown at runtime, the program needs to look for catch. So it make sense to choose whether to unwind the stack at runtime too.

like image 43
Petr Avatar answered Oct 22 '22 06:10

Petr