views:

185

answers:

7

I have an object on the stack for which I wish its destructor to skip some work when the destructor is being called because the stack is being unwound due to a specific exception being thrown through the scope of the object on the stack.

Now I could add a try catch block inside the scope of the stack item and catch the exception in question and notify the stack object to not run the work to be skipped an then rethrow the exception as follows:

RAII_Class pending;

try {
  doSomeWorkThatMayThrowException();
} catch (exceptionToSkipPendingDtor &err) {
  pending.notifySkipResourceRelease();
  throw;
}

However, I'm hoping there is a more elegant way to do this. For example imagine:

RAII_Class::~RAII_Class {
  if (detectExceptionToSkipPendingDtorBeingThrown()) {
    return;
  }
  releaseResource();
}
+8  A: 

You can almost do this with std::uncaught_exception(), but not quite.

Herb Sutter explains the "almost" better than I do: http://www.gotw.ca/gotw/047.htm

There are corner cases where std::uncaught_exception() returns true when called from a destructor but the object in question isn't actually being destroyed by the stack unwinding process.

You're probably better off without RAII because it doesn't match your use case. RAII means always clean up; exception or not.

What you want is much simpler: only release resource if an exception is not throw which is a simple sequence of functions.

explicitAllocateResource();
doSomeWorkThatMayThrowException();
explicitReleaseResource(); // skipped if an exception is thrown
                           // by the previous function.
Charles Bailey
Just need a way to query the uncaught_exception to see if it is of a particular type. It seems the way I've written it in the question is the only way to proceed.
WilliamKF
+5  A: 

I would do it the other way around - explicitly tell it to do its work if no exception was thrown:

RAII_Class pending;

doSomeWorkThatMayThrowException();

pending.commit(); // do or prepare actual work
Georg Fritzsche
But for other exceptions the work still needs to be done, so I'd still need a try/catch block for all other exception to do the commit() call too.
WilliamKF
@William: True, but you'd need that try/catch anyway if you want to catch at the current scope. The only addition is the `commit()` call, which is also more clear and maintainable than a hidden mechanism (assuming that was cleanly possible).
Georg Fritzsche
A: 

Looks like bool std::uncaught_exception(); does the trick if you want to have this behavior for every exception, not just special ones!

WilliamKF
A: 

You can do without a try-catch:

RAII_Class pending;
doSomeWorkThatMayThrowException();  // intentional: don't release if throw
pending.releaseResource();

Alternatively, you can try a little harder with RAII:

struct RAII_Class {
    template<class Op>
    void execute(Op op) {
        op();
        releaseResources();
    }

private:
    void releaseResources() { /* ... */ }
};

int main(int argc, char* argv[])
{
    RAII_Class().execute(doSomeWorkThatMayThrowException);
    return 0;
}
wilhelmtell
Yes, this works, but it has disadvantage that it puts the onus on the consumer to get it right instead of their being able to just place it on the stack and everything works as required.
WilliamKF
+3  A: 

This seems to circumvent the main reason to use RAII. The point of RAII is that if an exception happens in the middle of your code you can still release resources/be destructed properly.

If this isn;t the semantic you want, then don't use RAII.

So instead of:

void myFunction() {
    WrapperClass wc(acquireResource());

    // code that may throw
}

Just do:

void myFunction() {
    Resource r = acquireResource();

    // code that may throw

    freeResource(r);
}

If the code in the middle throws, the resource won't be freed. This is what you want, rather than keeping RAII (and keeping the name) but not implementing RAII semantics.

SoapBox
Other exceptions being thrown need to be sure to release the resource.
WilliamKF
A: 

I found this website with an interesting discussion about std::uncaught_exception() and an alternative solution to your question that seems much more elegant and correct to me:

http://www.gotw.ca/gotw/047.htm

//  Alternative right solution
//
T::Close() {
  // ... code that could throw ...
}

T::~T() /* throw() */ {
  try {
    Close();
  } catch( ... ) {
  }
}

In this way you're destructor does only one thing and you're protected against throwing an exception during an exception (which I assume is the problem you're trying to solve).

Scott Turley
A: 

Although it would be a kludge at best, if you own the code for the exception class you're interested in, you could add a static data member to that class (bool) that would be set to "true" in the constructor for objects of that class, and false in the destructor (might need to be an int that you increment/decrement instead). Then in the destructor of your RAII class, you can check std::uncaught_exception(), and if true, query the static data member in your exception class. If you get true (or > 0) back, you've got one of those exceptions--otherwise you ignore it.

Not very elegant, but it would probably do the trick (as long as you don't have multiple threads).

Drew Hall
Yes, while that works, thus far I'm sticking with the way I have it written in the question as I feel that is less of a kludge.
WilliamKF
@WilliamKF: I don't blame you--but I like Georg's idea of an explicit Commit() better!
Drew Hall