144

Will the following append() in the catch cause the rethrown exception to see the effect of append() being called?

try {  mayThrowMyErr();} catch (myErr &err) {  err.append("Add to my message here");  throw; // Does the rethrow exception reflect the call to append()?}

Similarly, if I rewrite it this way, will bit slicing occur if the actual exception is derived by myErr?

try {  mayThrowObjectDerivedFromMyErr();} catch (myErr &err) {  err.append("Add to my message's base class here");  throw err; // Do I lose the derived class exception and only get myErr?}
Gabe's user avatar
Gabe
87.1k13 gold badges144 silver badges238 bronze badges
askedMar 2, 2010 at 2:50
WilliamKF's user avatar
0

5 Answers5

179

In both cases, since you catch by reference, you are effectively altering the state of the original exception object (which you can think of as residing ina magical memory location which will stay valid during the subsequent unwinding --0x98e7058 in the example below). However,

  1. In the first case, since you rethrow withthrow; (which, unlikethrow err;, preserves the original exception object, with your modifications, in said "magical location" at0x98e7058)will reflect the call to append()
  2. In the second case, since you throw something explicitly, acopy oferr will be created then thrown anew (at a different "magical location"0x98e70b0 -- because for all the compiler knowserr could be an object on the stack about to be unwinded, likee was at0xbfbce430, not in the "magical location" at0x98e7058), soyou will lose derived-class-specific data during the copy-construction of a base class instance.

Simple program to illustrate what's happening:

#include <stdio.h>struct MyErr {  MyErr() {    printf("  Base default constructor, this=%p\n", this);  }  MyErr(const MyErr& other) {    printf("  Base copy-constructor, this=%p from that=%p\n", this, &other);  }  virtual ~MyErr() {    printf("  Base destructor, this=%p\n", this);  }};struct MyErrDerived : public MyErr {  MyErrDerived() {    printf("  Derived default constructor, this=%p\n", this);  }  MyErrDerived(const MyErrDerived& other) {    printf("  Derived copy-constructor, this=%p from that=%p\n", this, &other);  }  virtual ~MyErrDerived() {    printf("  Derived destructor, this=%p\n", this);  }};int main() {  try {    try {      MyErrDerived e;      throw e;    } catch (MyErr& err) {      printf("A Inner catch, &err=%p\n", &err);      throw;    }  } catch (MyErr& err) {    printf("A Outer catch, &err=%p\n", &err);  }  printf("---\n");  try {    try {      MyErrDerived e;      throw e;    } catch (MyErr& err) {      printf("B Inner catch, &err=%p\n", &err);      throw err;    }  } catch (MyErr& err) {    printf("B Outer catch, &err=%p\n", &err);  }  return 0;}

Result:

  Base default constructor, this=0xbfbce430  Derived default constructor, this=0xbfbce430  Base default constructor, this=0x98e7058  Derived copy-constructor, this=0x98e7058 from that=0xbfbce430  Derived destructor, this=0xbfbce430  Base destructor, this=0xbfbce430A Inner catch, &err=0x98e7058A Outer catch, &err=0x98e7058  Derived destructor, this=0x98e7058  Base destructor, this=0x98e7058---  Base default constructor, this=0xbfbce430  Derived default constructor, this=0xbfbce430  Base default constructor, this=0x98e7058  Derived copy-constructor, this=0x98e7058 from that=0xbfbce430  Derived destructor, this=0xbfbce430  Base destructor, this=0xbfbce430B Inner catch, &err=0x98e7058  Base copy-constructor, this=0x98e70b0 from that=0x98e7058  Derived destructor, this=0x98e7058  Base destructor, this=0x98e7058B Outer catch, &err=0x98e70b0  Base destructor, this=0x98e70b0

Also see:

answeredMar 2, 2010 at 3:12
vladr's user avatar
Sign up to request clarification or add additional context in comments.

Comments

35

This question is rather old and has an answer appropriate to the time it was asked.However, I just want to add a note on how to do proper exception handling sinceC++11 and I believe this corresponds very well to what you were trying to achieve with your append function:

Usestd::nested_exception andstd::throw_with_nested

It is described on StackOverflowhere andhere, how you canget a backtrace on your exceptions inside your code without need for a debugger or cumbersome logging, by simply writing a proper exception handler which will rethrow nested exceptions.

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace!You may also take a look at myMWE on GitHub, where a backtrace would look something like this:

Library API: Exception caught in function 'api_function'Backtrace:~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
answeredDec 3, 2017 at 13:21
GPMueller's user avatar

Comments

9

Yes, rethrowing rethrows the original exception object, which you have modified by a reference. You can also catch a base class reference, modify by it and still be able to rethrow the original derived exception type bythrow;.

answeredMar 2, 2010 at 3:07
Tronic's user avatar

Comments

3

The C++2003 Standart, was at the time the active standard when that question was asked. Please be aware that C++ Language has a standard.

https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1905.pdf

p.351, p.15.4:

When the handler declares a non-constant object, any changes to that object will not affect the temporary object that was initialized by execution of the throw-expression. When the handler declares a reference to a non-constant object, any changes to the referenced object are changes to the temporary object initialized when the throw expression was executed and will have effect should that object be rethrown.

So you can be sure that any proper C++ compiler of C++2003 will not create any extra copies...

Another way you can play with code snippets like presented below and observe the exact address of the object, and as each object has a unique address in C++, that implies that the object is the same. But it only enhances your confidence.

The only way to be 100% sure - look into Standart for Programming Language.

#include <iostream>using std::cout;class A{};void f1() {    throw A();}void f2(){    try {        f1();    }    catch(A& obj) {        cout << "f1 obj: " << &obj << "\n";        throw;    }}void f3(){    try {        f2();    }    catch(A& obj) {        cout << "f3 obj: " << &obj << "\n";        throw;    }}int main(){    try {        f3();    }    catch(A& obj)    {        cout << "main obj: " << &obj;    }        return 0;}
answeredJul 6, 2022 at 6:55
Konstantin Burlachenko's user avatar

Comments

1

for first question, yes.

but for second, refer to Vlad answer.you will need to carefully design your exception object to handle copy ctor. by convention, base class doesn't recognize its child so you will most likely lose the additional data carried by derived class.

answeredMar 2, 2010 at 3:09
YeenFei's user avatar

Comments

Your Answer

Sign up orlog in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to ourterms of service and acknowledge you have read ourprivacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.