Boost logo

Boost :

Subject: [boost] Proposed new RAII Library
From: Matus Chochlik (chochlik_at_[hidden])
Date: 2012-09-15 05:36:13


On Sat, Sep 15, 2012 at 11:11 AM, Sergey Cheban <s.cheban_at_[hidden]> wrote:
> 15.09.2012 5:27, John Bytheway пишет:
[snip
>> std::uncaught_exception is very dangerous to use in this way. See
>>
>> http://www.gotw.ca/gotw/047.htm
>
> This article is not about my idea. I do not propose to throw from the
> destructor. I propose to use uncaught_exception() to determine whether the
> resources must be freed on the scope exit or not.

It is not only about the detection if your code can throw or not,
but about the fact that if you use your exception guard
in a destructor, it will behave differently if the destructor
is called during stack unwinding when an exception propagates
and differently when the destructor is called under "normal"
circumstances (i.e. no exception being thrown)
see below:

>
> I propose to use the following
>
> {
> HANDLE h = CheckHandle( CreateFile(...) ); //CheckHandle() may throw
> boost::exception_guard guard( [&](){
> CloseHandle(h);
> } );
>
> //other code that can throw
>
> return h; //note that h must not be closed
> }
>
> instead of the following
>
> {
> bool bDontClose = false;
> HANDLE h = CheckHandle( CreateFile(...) ); //CheckHandle() may throw
> boost::scope_guard guard( [&](){
> if( !bDontClose )
> CloseHandle(h);
> } );
>
> //other code that can throw
>
> bDontClose = true;
> return h; //note that h must not be closed
>
> }
>
>> In short: your function will misbehave if called from a destructor
>> during stack unwinding.
>
> It seems strange to me. Destructors are the only places where
> uncaught_exception() may return true, right? What's wrong with the
> following?
>
> ~exception_guard()
> {
> if( uncaught_exception() )
> f();
> }
>
> Is it less safe than the following?
>
> ~scope_guard()
> {
> f();
>
> }
>
Whats wrong is (if I'm not teribly mistaken) is best
illustrated by the "transaction" example in the GOTW:

    Transaction::~Transaction() {
      if( uncaught_exception() ) {
        RollBack();
      }
    }

If the transaction is used in a destructor:

    U::~U() {
      try {
        Transaction t( /*...*/ );
        // do work
      } catch( ... ) {
        // clean up
      }
    }

Then the transaction is *always* rolled back (even if it
could complete successfully) when U::~U is called
during exception propagation:

{
    U u;
    // ...
    something_that_throws();
} // <- the transaction in U::~U is executed and always rolled back
  // regardless if it completes successfully

Best,

Matus


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk