30.09.2013 2:55, Emil Dotchevski:
1. scope(failure/success) runs specified code block. Sometimes such code block can be reused - in that case custom guard object is preferred over copy-pasting code.
That is still the scope failure use case, which presumably has to deal with exceptions in the failure branch.
What do you mean? Every use case of uncaught_exception_count has to deal something with exceptions. And yes, that use case is similar to scope(failure/success), but it cannot be implemented on top of it. In order to implement it user need a tool: uncaught_exception_count/unwinding_indicator. And I think that tool is closer to Boost.Exception than to Boost.ScopeExit.
2. Temporary objects: log() << may_throw() << endl; log() returns some temporary object which does some finalizing work in destructor. If that destructor is called due to stack unwinding - then finalizing part will be different.
Here, if may_throw() throws, ~log() must not throw. Therefore it is possible for the user to not be notified about a logging failure. If it is critical for the user to be notified about all logging failures, then then ~log() must not do any logging. How critical this notification is doesn't depend on whether or not may_throw() emits an exception.
It is not only about logging. Temporary object may do some job related to invariants. For instance it can be used to get "strong guarantee" in some cases: may_throw( x.temp_inner_invariant() ); destructor of object returned by temp_inner_invariant can restore original state on exception from may_throw. Or it can be opposite - do some invariant keeping job on success and do nothing on failure (or maybe replace inner data with cheap default to get something like "basic guarantee"). Concrete example: suppose we have class UnsafeString: class UnsafeString { vector<char> data; size_t length; ... invariant is length == strlen(&data[0]); Such string can be filled by API which accepts only char* in following way: UnsafeString s; unsafe_api( s.get_buffer(1024) ); get_buffer returns auxiliary wrapper which implicitly converts to char* and in destructor maintains invariant of UnsafeString. If unsafe_api would throw exception - then auxiliary wrapper can use unwinding_indicator to spot it and select appropriate approach (restore to original or restore to cheap default value).
3. Stack objects which do work in destructor that may fail. Traditional example is File object which requires flush(may fail) and release handle at the end of lifetime. Typical solution is to place .flush() manually and release handle in destructor: { File a,b; // ... b.flush(); // may throw a.flush(); // may throw }
The motivation here is to support throwing destructors. This becomes a self-fulfilling prophecy: you write destructors that may throw, which forces everyone else to deal with that.
I am talking about throwing only from destructors of stack objects. There are many reasons to not throw from objects which are part of others, for instance from element of std::vector or from element of plain array. I do not offer to throw from every kind of destructor. But it is OK to throw from desctructor of "stack object" as long as you do that conditionally based on unwinding indicator. That can be controversial - but it is just one of examples of unwinding indicator usage. It is up to developer to decide whenever use it or not.
It is incorrect for objects that are no longer needed to be able to fail to go away, therefore destructors must not throw.
Why? Semantically it is exactly same to placing flush by hands. -- Evgeny Panasyuk