6

I'm writing a drop-replacement for a container, and I'm trying to get all the exception-guarantees in place. I'm currently writing the clear method, and I want it to complete as much as possible, and to always leave the container in a consistent state, even if one of the destructors throw an exception. I also want to rethrow the exception after I'm done cleaning, preferably without slicing.

This brings me to the question; when is an exception destructed? Lets have a look at one attempt: This is simplified for the example.

void container::clear()
{
    bool had_exception = false;
    std::exception* exp;
    internal_set_empty(); // this cant throw
    while( ! internal_done() )
    {
        try
        {
            internal_destruct_next(); // this might throw if T::~T() throws
        }
        catch( std::exception& e )
        {
            had_exception = true;
            exp = &e;
        }
    }
    if( had_exception )
        throw *exp;
}

I expect this to fail badly, because the exception is probably destructed when it is considered handled, and this doesn't technically rethrow.

Another attempt would be taking a copy of the exception, something that I expect would slice.

Is there a way to extend the lifetime of the exception so I can rethrow it later? If possible, I would also like to be able to rethrow exceptions caught via catch(...) .

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
sp2danny
  • 7,488
  • 3
  • 31
  • 53
  • http://en.cppreference.com/w/cpp/error/exception_ptr could be handy if your scenario can work. โ€“ Mat Aug 20 '15 at 15:36
  • @Mat `exception_ptr` seems like exactly what I was looking for, but it seems like there is a more fundamental problem. โ€“ sp2danny Aug 20 '15 at 15:40

2 Answers2

4

even if one of the destructors throw an exception

Cannot be done. Destructors are nothrow for a reason- you can't recover from a throwing destructor. It's clear that you don't do anything to clean up the objects whose destructor threw- because how could you even try to do that- but that also means that you've just left them hanging around in a zombie state.

On your more specific question, you could use recursion to stay inside the catch's stack frame to try to keep deleting elements, or you could also use std::exception_ptr and friends in C++11 which are intended for transporting exceptions around.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Thanks. I also found [this](http://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor) to be interesting โ€“ sp2danny Aug 20 '15 at 15:48
1

The current usage to pass the exception to a caller is to rethrow it in the catch bloc. Assuming you have good reasons not to do it, the only other correct way is to use a std::exception_ptr. With a mere std::exception* there are high risk that the exception is destroyed and its memory deallocated before you can throw it again.

Standard C++ 2011 draft (n4296) says at 15.1 Throwing an exception ยง 4 : 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 that refers to the exception object is destroyed, whichever is later. In the former case, the destruction occurs when the handler exits, immediately after the destruction of the object declared in the exception-declaration in the handler, if any. In the latter case, the destruction occurs before the destructor of std::exception_ptr returns. The implementation may then deallocate the memory for the exception object; any such deallocation is done in an unspecified way (emphasize mine)

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252