Andrew Durward
Andrew Durward

Reputation: 3861

Is the lifetime of an exception affected by other exceptions?

As a follow-up to my previous question:

If I change the code as follows:

struct ExceptionBase : virtual std::exception{};
struct SomeSpecificError : virtual ExceptionBase{};
struct SomeOtherError : virtual ExceptionBase{};

void MightThrow();
void HandleException();
void ReportError();

int main()
{
  try
  {
    MightThrow();
  }
  catch( ... )
  {
    HandleException();
  }
}

void MightThrow()
{
  throw SomeSpecificError();
}

void HandleException()
{
  try
  {
    throw;
  }
  catch( ExceptionBase const & )
  {
    // common error processing
  }

  try
  {
    throw;
  }
  catch( SomeSpecificError const & )
  {
    // specific error processing
  }
  catch( SomeOtherError const & )
  {
    // other error processing
  }

  try
  {
    ReportError();
  }
  catch( ... )
  {
  }
}

void ReportError()
{
  throw SomeOtherError();
}

The "last handler" for the original exception (i.e. the one in main) has not exited when the second exception is thrown, so are both exceptions active? Is the original exception still available once we leave the handler for the second exception?

Upvotes: 2

Views: 197

Answers (3)

aschepler
aschepler

Reputation: 72271

C++11 (N3242):

15.1p4: The memory for the exception object is allocated in an unspecified way, except as noted in 3.7.4.1. If a handler exits by rethrowing, control is passed to another handler for the same exception. The exception object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type std::exception_ptr (18.8.5) that refers to the exception object is destroyed, whichever is later.

(std::exception_ptr is a C++11 feature, and isn't used in your example code.)

15.3p7: A handler is considered active when initialization is complete for the formal parameter (if any) of the catch clause. ... A handler is no longer considered active when the catch clause exits or when std::unexpected() exits after being entered due to a throw.

15.3p8: The exception with the most recently activated handler that is still active is called the currently handled exception.

15.1p8: A throw-expression with no operand rethrows the currently handled exception (15.3).

Or equivalently, I think, throw; always refers to the exception caught by the innermost catch block which is currently executing. Except that I haven't defined 'innermost' and 'executing' as carefully as the Standard defined all its terms above.

And yes, more than one exception object can be allocated at a time, and C++ is required to make sure they live long enough to do "the right thing" when you try to rethrow.

Upvotes: 2

Nemo
Nemo

Reputation: 71525

There is only one "active exception" at a time. When you throw another exception in an exception handler, in effect you are changing the type of the exception that is being propagated up the stack.

(As an aside, is all this necessary? Do you really find this code easy to read?)

[update]

As for the standard reference... ISO/IEC 14882:2003, section 15.3 [except.handle], paragraph 8 reads:

An exception is considered handled upon entry to a handler. [Note: the stack will have been unwound at that point. ]

So another way to say this is, as soon as you enter the catch block, the original exception is no longer active.

Also, the uncaught_exception() function will return false as soon as the catch block is entered. Section 15.5.3 [except.uncaught] reads:

The function

bool uncaught_exception() throw()

returns true after completing evaluation of the object to be thrown until completing the initialization of the exception-declaration in the matching handler (18.6.4). This includes stack unwinding. If the exception is rethrown (15.1), uncaught_exception() returns true from the point of rethrow until the rethrown exception is caught again.

[update 2]

Also relevant is section 15.3 paragraph 4:

The memory for the temporary copy of the exception being thrown is allocated in an unspecified way, except as noted in 3.7.3.1. The temporary persists as long as there is a handler being executed for that exception. In particular, if a handler exits by executing a throw; statement, that passes control to another handler for the same exception, so the temporary remains. When the last handler being executed for the exception exits by any means other than throw; the temporary object is destroyed and the implementation may deallocate the memory for the temporary object; any such deallocation is done in an unspecified way. The destruction occurs immediately after the destruction of the object declared in the exception-declaration in the handler.

So the original exception is destroyed as soon as the handler is exited via any means other than a naked throw;. So if you throw some other exception, that exits the handler and destroys the original exception.

Upvotes: 0

cHao
cHao

Reputation: 86506

Not sure how standard it is, but if i add a throw; right before the end of HandleException and compile with g++, the resulting program tells me this:

root@xxxx [~/code]# ./a.out
terminate called after throwing an instance of 'SomeSpecificError'
  what():  17SomeSpecificError
Aborted

Note the exception type. That's the exception that was thrown in MightThrow, not the one from ReportError.

VS2010 reports a SomeSpecificError as well.

Upvotes: 1

Related Questions