Re: [boost] [scope_exit] D-style scope(failure) and scope(success) in C++
On 02.10.2012 16:53, Evgeny Panasyuk wrote:
Currently BOOST_SCOPE_FAILURE and BOOST_SCOPE_SUCCESS are implemented based on boost_1_51_0/boost/scope_exit.hpp by copy-paste and replace (just for proof-or-concept, of course it is possible solution with much less duplication). Semantically meaningful changes can be viewed as diff at https://github.com/panaseleus/stack_unwinding/commit/d5513404323490ea99d9b6b... Any progress on this?
-- Sergey Cheban
On Thu, Sep 26, 2013 at 5:58 AM, Sergey Cheban
On 02.10.2012 16:53, Evgeny Panasyuk wrote:
Currently BOOST_SCOPE_FAILURE and BOOST_SCOPE_SUCCESS are implemented based on boost_1_51_0/boost/scope_exit.hpp by copy-paste and replace (just for proof-or-concept, of course it is possible solution with much less duplication). Semantically meaningful changes can be viewed as diff at
https://github.com/panaseleus/stack_unwinding/commit/d5513404323490ea99d9b6b...
Any progress on this?
As far as I understand it, this cannot be implemented (at least in a portable way). See: http://www.boost.org/doc/libs/1_54_0/libs/scope_exit/doc/html/scope_exit/alt... Boost.ScopeExit is similar to scope(exit) feature built into the D programming language. A curious reader may notice that the library does not implement scope(success) and scope(failure) of the D language. Unfortunately, these are not possible in C++ because failure or success conditions cannot be determined by callingstd::uncaught_exception (see Guru of the Week #47 for details about std::uncaught_exception and if it has any good use at all). However, this is not a big problem because these two D's constructs can be expressed in terms of scope(exit) and a boolcommit variable (similarly to some examples presented in the Tutorial section). HTH, --Lorenzo
27.09.2013 1:55, Lorenzo Caminiti:
Any progress on this?
As far as I understand it, this cannot be implemented (at least in a portable way). See:
I have made platform-specific implementation: https://github.com/panaseleus/stack_unwinding. It is tested it on {Clang 3.2, GCC 3.4.6, GCC 4.1.2, GCC 4.4.6, GCC 4.4.7, MSVC2005SP1, MSVC2008SP1, MSVC2010SP1, MSVC2012} x {x32, x64} and some others - which fulfils Boost portability requirements( http://www.boost.org/development/requirements.html#Portability ): " A library's implementation must if possible be portable and not restricted to a particular compiler or operating system. If a portable implementation is not possible, non-portable constructions are acceptable if reasonably easy to port to other environments, and implementations are provided for at least two popular operating systems (such as UNIX and Windows). " I have posted it here twice to different libraries with no success: 1. [boost][scope_exit]: http://boost.2283326.n4.nabble.com/scope-exit-D-style-scope-failure-and-scop... 2. [boost][exception]: http://boost.2283326.n4.nabble.com/exception-uncaught-exception-count-scope-... It is partly included in Boost.Log since 1.54: http://ci.boost.org/svn-trac/browser/trunk/libs/log/src/unhandled_exception_... . But for the purposes of scope(failure) and scope(success) it is preferable to put it as header-only. If library maintainer of [exception] or [scope_exit] would agree to integrate uncaught_exception_count - then we may be lucky to get it released in 1.56. -- Evgeny Panasyuk
On Thu, Sep 26, 2013 at 3:39 PM, Evgeny Panasyuk
If library maintainer of [exception] or [scope_exit] would agree to integrate uncaught_exception_count - then we may be lucky to get it released in 1.56.
I don't understand what does "integrate" mean in this case. -- Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
27.09.2013 2:45, Emil Dotchevski:
On Thu, Sep 26, 2013 at 3:39 PM, Evgeny Panasyuk
wrote: If library maintainer of [exception] or [scope_exit] would agree to integrate uncaught_exception_count - then we may be lucky to get it released in 1.56.
I don't understand what does "integrate" mean in this case.
Would like to see it as part of appropriate library. -- Evgeny Panasyuk
On Thu, Sep 26, 2013 at 3:51 PM, Evgeny Panasyuk
27.09.2013 2:45, Emil Dotchevski:
On Thu, Sep 26, 2013 at 3:39 PM, Evgeny Panasyuk
wrote: If library maintainer of [exception] or [scope_exit] would agree to integrate uncaught_exception_count - then we may be lucky to get it released in 1.56.
I don't understand what does "integrate" mean in this case.
Would like to see it as part of appropriate library.
Perhaps I'm missing something, but if it is a facility that aids the scope_exit library, it should be part of the scope_exit library. Do you feel that it should be part of Boost Exception? -- Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
27.09.2013 3:08, Emil Dotchevski:
Perhaps I'm missing something, but if it is a facility that aids the scope_exit library, it should be part of the scope_exit library. Do you feel that it should be part of Boost Exception?
There are two parts: 1. function uncaught_exception_count 2. scope(failure) and scope(success) which are built on top of uncaught_exception_count I have in mind two approaches: a) Add both into Boost.ScopeExit or b) As a first step add uncaught_exception_count into Boost.Exception - as it is closely related to it's theme. And then scope(failure)/scope(success) will go into Boost.ScopeExit. At that message - http://boost.2283326.n4.nabble.com/exception-uncaught-exception-count-scope-... - there is link to proposal by Herb Sutter about similar functionality. -- Evgeny Panasyuk
On Thu, Sep 26, 2013 at 4:20 PM, Evgeny Panasyuk
27.09.2013 3:08, Emil Dotchevski:
Perhaps I'm missing something, but if it is a facility that aids the scope_exit library, it should be part of the scope_exit library. Do you feel that it should be part of Boost Exception?
There are two parts: 1. function uncaught_exception_count 2. scope(failure) and scope(success) which are built on top of uncaught_exception_count
I have in mind two approaches: a) Add both into Boost.ScopeExit or b) As a first step add uncaught_exception_count into Boost.Exception - as it is closely related to it's theme. And then scope(failure)/scope(success) will go into Boost.ScopeExit.
Is there a use case for uncaught_exception_count other than to implement D-style scope-exit/-failure? -- Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Thursday 26 September 2013 16:24:21 Emil Dotchevski wrote:
On Thu, Sep 26, 2013 at 4:20 PM, Evgeny Panasyuk
wrote: 27.09.2013 3:08, Emil Dotchevski:
Perhaps I'm missing something, but if it is a facility that aids the scope_exit library, it should be part of the scope_exit library. Do you feel that it should be part of Boost Exception?
There are two parts: 1. function uncaught_exception_count 2. scope(failure) and scope(success) which are built on top of uncaught_exception_count
I have in mind two approaches: a) Add both into Boost.ScopeExit or b) As a first step add uncaught_exception_count into Boost.Exception - as it is closely related to it's theme. And then scope(failure)/scope(success) will go into Boost.ScopeExit.
Is there a use case for uncaught_exception_count other than to implement D-style scope-exit/-failure?
In Boost.Log, I use it to implement my own guard objects which must perform some finalizing work at destructor. With uncaught_exception_count I am able to detect whether the destructor is invoked as a result of an exception and cancel the work. uncaught_exception_count is an improvement of std::uncaught_exception. IMHO, its place is Boost.Exception.
On Thu, Sep 26, 2013 at 11:37 PM, Andrey Semashev
On Thursday 26 September 2013 16:24:21 Emil Dotchevski wrote:
On Thu, Sep 26, 2013 at 4:20 PM, Evgeny Panasyuk
wrote: 27.09.2013 3:08, Emil Dotchevski:
Perhaps I'm missing something, but if it is a facility that aids the scope_exit library, it should be part of the scope_exit library. Do you feel that it should be part of Boost Exception?
There are two parts: 1. function uncaught_exception_count 2. scope(failure) and scope(success) which are built on top of uncaught_exception_count
I have in mind two approaches: a) Add both into Boost.ScopeExit or b) As a first step add uncaught_exception_count into Boost.Exception - as it is closely related to it's theme. And then scope(failure)/scope(success) will go into Boost.ScopeExit.
Is there a use case for uncaught_exception_count other than to implement D-style scope-exit/-failure?
In Boost.Log, I use it to implement my own guard objects which must perform some finalizing work at destructor. With uncaught_exception_count I am able to detect whether the destructor is invoked as a result of an exception and cancel the work.
Isn't this the same use case as scope-failure? (I don't buy the "must" in "finalizing work in a destructor" but that's the same as scope-failure, too.) -- Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Friday 27 September 2013 00:23:08 Emil Dotchevski wrote:
On Thu, Sep 26, 2013 at 11:37 PM, Andrey Semashev
wrote: On Thursday 26 September 2013 16:24:21 Emil Dotchevski wrote:
On Thu, Sep 26, 2013 at 4:20 PM, Evgeny Panasyuk
wrote: 27.09.2013 3:08, Emil Dotchevski:
Perhaps I'm missing something, but if it is a facility that aids the scope_exit library, it should be part of the scope_exit library. Do you feel that it should be part of Boost Exception?
There are two parts: 1. function uncaught_exception_count 2. scope(failure) and scope(success) which are built on top of uncaught_exception_count
I have in mind two approaches: a) Add both into Boost.ScopeExit or b) As a first step add uncaught_exception_count into Boost.Exception - as it is closely related to it's theme. And then scope(failure)/scope(success) will go into Boost.ScopeExit.
Is there a use case for uncaught_exception_count other than to implement D-style scope-exit/-failure?
In Boost.Log, I use it to implement my own guard objects which must perform some finalizing work at destructor. With uncaught_exception_count I am able to detect whether the destructor is invoked as a result of an exception and cancel the work.
Isn't this the same use case as scope-failure?
The guard object in Boost.Log is not a scope guard but is a temporary object. But the use of uncaught_exception_count is very similar. I cannot reuse scope_exit(success) if you imply that.
27.09.2013 3:24, Emil Dotchevski:
Is there a use case for uncaught_exception_count other than to implement D-style scope-exit/-failure?
scope(failure)/scope(success) would be main use case, however - there are several others: 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. 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. I think Andrey Semashev has something similar at Boost.Log. 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 } If b.flush() will throw then a.flush() will not happen but only release of file handle at a.~File(). Same effect can be achieved automatically using uncaught_exception_count/unwinding_indicator: { File a,b; // ... // conditional flush of b and a in destructors. } -- Evgeny Panasyuk
On Sun, Sep 29, 2013 at 2:23 PM, Evgeny Panasyuk
27.09.2013 3:24, Emil Dotchevski:
Is there a use case for uncaught_exception_count other than to implement D-style scope-exit/-failure?
scope(failure)/scope(success) would be main use case, however - there are several others:
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. I don't object to scope exit/failure dealing with exceptions any way its designers think it should.
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.
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. It is incorrect for objects that are no longer needed to be able to fail to go away, therefore destructors must not throw. So, I'd consider flush()ing elsewhere. -- Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
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
On Sunday 29 September 2013 15:55:01 Emil Dotchevski wrote:
On Sun, Sep 29, 2013 at 2:23 PM, Evgeny Panasyuk
wrote: 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.
In case of Boost.Log, ~log() would actually push the log record to the library for processing (applying final formatting, writing to files/console, etc.). If the streaming statement fails with an exception, ~log() must not do anything and let the exception propagate. OTOH, when the streaming expression completes normally, the record processing may throw itself, which makes ~log() a throwing destructor. So you see, no errors are lost, but in some cases work must be suppressed for correct behavior. Writing throwing destructors is discouraged, everyone knows that. But sometimes you just need it because it offers benefits compared to other approaches. unhandled_exception_count() is crucial to make that possible. There are restrictions for such classes, e.g. they should not be used as members of other classes or allocated on heap. But log() above is never intended to be used that way.
On Sun, Sep 29, 2013 at 8:53 PM, Andrey Semashev
On Sunday 29 September 2013 15:55:01 Emil Dotchevski wrote:
On Sun, Sep 29, 2013 at 2:23 PM, Evgeny Panasyuk Writing throwing destructors is discouraged, everyone knows that. But sometimes you just need it because it offers benefits compared to other approaches.
It also has drawbacks. :) Perhaps "need" is an overstatement here, I'm sure there are other ways to do this even if we insist on using the same syntax. I didn't intend to create an argument, but I think that it is a mistake to open this Pandora's box, so I don't want to provide support for it in the exception library. -- Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Sunday 29 September 2013 21:55:44 Emil Dotchevski wrote:
On Sun, Sep 29, 2013 at 8:53 PM, Andrey Semashev
wrote: On Sunday 29 September 2013 15:55:01 Emil Dotchevski wrote:
On Sun, Sep 29, 2013 at 2:23 PM, Evgeny Panasyuk
Writing throwing destructors is discouraged, everyone knows that. But sometimes you just need it because it offers benefits compared to other approaches.
It also has drawbacks. :)
Perhaps "need" is an overstatement here, I'm sure there are other ways to do this even if we insist on using the same syntax.
I didn't intend to create an argument, but I think that it is a mistake to open this Pandora's box, so I don't want to provide support for it in the exception library.
Ok. In that case, how about making unhandled_exception_count() a member of boost/detail, so that multiple libraries can use it?
30.09.2013 8:55, Emil Dotchevski:
Writing throwing destructors is discouraged, everyone knows that. But sometimes you just need it because it offers benefits compared to other approaches.
It also has drawbacks. :)
Perhaps "need" is an overstatement here, I'm sure there are other ways to do this even if we insist on using the same syntax.
If that is possible then scope(failure/success) also can be implemented in that way (without unwinding_indicator). Many have tried to do that in portable C++ with no success.
I didn't intend to create an argument, but I think that it is a mistake to open this Pandora's box, so I don't want to provide support for it in the exception library.
1. Throwing destructors is just one use case for scope(success)-like objects. But there are other use cases with scope(failure)-like objects which do not throw anything from destructor. 2. There is ISO proposal by Herb Sutter - http://isocpp.org/files/papers/N3614.pdf - for adding similar functionality: std::unwinding_exception(). 3. There is article by Jon Kalb and Dave Abrahams regarding throwing destructors: "Evil, or Just Misunderstood?" http://cpp-next.com/archive/2012/08/evil-or-just-misunderstood/ -- Evgeny Panasyuk
participants (5)
-
Andrey Semashev
-
Emil Dotchevski
-
Evgeny Panasyuk
-
Lorenzo Caminiti
-
Sergey Cheban