
On Sat, Sep 15, 2012 at 11:11 AM, Sergey Cheban <s.cheban@drweb.com> wrote:
15.09.2012 5:27, John Bytheway пишет: [snip
std::uncaught_exception is very dangerous to use in this way. See
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