Re: [boost] Review Request: Boost Exception

Message: 5 Date: Tue, 14 Nov 2006 20:38:43 +0200 From: "Peter Dimov" <pdimov@mmltd.net> Subject: Re: [boost] Review Request: Boost Exception To: <boost@lists.boost.org> Message-ID: <00ee01c7081c$1f94f230$6607a8c0@pdimov2> Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=original
David Abrahams wrote:
"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
> "Peter Dimov" <pdimov@mmltd.net> writes: >
>>> Moving the catch clause to a destructor doesn't seem an improvement >>> to me.
> > Using a catch clause to add information to a propagating exception > feels syntactically heavy to me, but of course others may disagree. > And it may also be that without language support, there's not > really a good way to avoid that weight. I'm just expressing an > aesthetic preference here.
Won't you need to prepare all the extra information even if nothing throws?
No, you could do it lazily.
I'm afraid that I need to be shown an example in order to "get it". :-)
I think Mr Abrahams is referring to an idea similar to this: (please ignore the class name, it is a placeholder only) void read_file() { boost::exception_info e(..., filename, current_pos, is_connected); ... e.success(); return; } e.success() sets a flag to true, which prevents the generation of error information when the object is destroyed. To remove the e.success(), you'd need to detect whether the stack was being unwound due to an exception or due to a natural return. This is where language support (or compiler-dependent tricks) would be required. Nested exceptions complicate that picture. The other issue is how to handle return values cleanly. You could make success() a pass-through function so you could handle return values with a shorter syntax: int sum(int a, int b) { error_info e; ... return e.success(a+b); } template <typename T> T& error_info::success(T& value) { flag = true; return rhs; } (special care needed to avoid copying and to handle references/temporaries etc). Ultimately, this approach is cleaner when there are more exit points due to exceptions than from return statements. Otherwise, you could forget to call success() at one of the return sites and hit a performance issue as it generates the exception info. Regards, Matthew Herrmann

Matthew Herrmann wrote:
To remove the e.success(), you'd need to detect whether the stack was being unwound due to an exception or due to a natural return. This is where language support (or compiler-dependent tricks) would be required. Language support exists. There is a function that returns true if the stack is currently being unwound.
It is broken in VC++6 (always returns false), but aside from that I believe it's quite well supported, though little known. Sebastian Redl

Matthew Herrmann <matthew.herrmann@zomojo.com> writes:
I think Mr Abrahams is referring to an idea similar to this: (please ignore the class name, it is a placeholder only)
void read_file() { boost::exception_info e(..., filename, current_pos, is_connected); ... e.success(); return; }
e.success() sets a flag to true, which prevents the generation of error information when the object is destroyed.
No, that's not quite what I meant. void tag_exception(boost::exception& x) { // do something to x } void read_file() { boost::adorn_exception e( boost::bind( tag_exception, _1 ) ); ... }
To remove the e.success(), you'd need to detect whether the stack was being unwound due to an exception or due to a natural return. This is where language support (or compiler-dependent tricks) would be required.
We already have that facility: std::uncaught_exception(). -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Matthew Herrmann <matthew.herrmann@zomojo.com> writes:
I think Mr Abrahams is referring to an idea similar to this: (please ignore the class name, it is a placeholder only)
void read_file() { boost::exception_info e(..., filename, current_pos, is_connected); ... e.success(); return; }
e.success() sets a flag to true, which prevents the generation of error information when the object is destroyed.
No, that's not quite what I meant.
void tag_exception(boost::exception& x) { // do something to x }
void read_file() { boost::adorn_exception e( boost::bind( tag_exception, _1 ) ); ... }
Could you provide more detail on the "do something to x" part? It seems to me that it would require something similar to the boost::exception::pimpl class, in that it must be able to store arbitrary values, but unlike boost::exception::pimpl it would have to keep some kind of proxy for each stored object, to be evaluated later if something throws. Wouldn't that amount to executing a lot of code and using a lot of storage, even if nothing throws?

"Emil Dotchevski" <emildotchevski@hotmail.com> writes:
David Abrahams wrote:
void tag_exception(boost::exception& x) { // do something to x }
void read_file() { boost::adorn_exception e( boost::bind( tag_exception, _1 ) ); ... }
Could you provide more detail on the "do something to x" part?
Whatever you'd do in the catch block with the current design, you could do here.
It seems to me that it would require something similar to the boost::exception::pimpl class, in that it must be able to store arbitrary values, but unlike boost::exception::pimpl it would have to keep some kind of proxy for each stored object, to be evaluated later if something throws.
Wouldn't that amount to executing a lot of code and using a lot of storage, even if nothing throws?
Sorry, I don't see it. Maybe I'm just blind. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
void tag_exception(boost::exception& x) { // do something to x }
void read_file() { boost::adorn_exception e( boost::bind( tag_exception, _1 ) ); ... }
This could work, if you find a way to write ~adorn_exception. You'd have void tag_exception( boost::exception & x, std::string const & fn ) { x << error_info<tag_errno>( errno ); x << error_info<tag_filename>( fn ); } void read_file( std::string const & fn ) { boost::adorn_exception e( boost::bind( tag_exception, _1, boost::ref( fn ) ) ); // do something that can throw e.dismiss(); } instead of the straightforward void read_file( std::string const & fn ) { try { // do something that can throw } catch( boost::exception & x ) { x << error_info<tag_errno>( errno ); x << error_info<tag_filename>( fn ); throw; } } I prefer the second.

Peter Dimov said: (by the date of Wed, 15 Nov 2006 15:54:14 +0200)
e.dismiss();
why this? destructor of e does the work. No need to e.dismiss() Dave pointed out that std::uncaught_exception() inside e's destructor can check the conditions upon which the destructor is called and to the necessary work depending on it.
void read_file( std::string const & fn ) { try{..} catch( boost::exception & x ){..} }
I prefer the second.
it's just a matter of preference, so perhaps both ways should work. -- Janek Kozicki |

Janek Kozicki wrote:
Peter Dimov said: (by the date of Wed, 15 Nov 2006 15:54:14 +0200)
e.dismiss();
why this? destructor of e does the work. No need to e.dismiss() Dave pointed out that std::uncaught_exception() inside e's destructor can check the conditions upon which the destructor is called and to the necessary work depending on it.
uncaught_exception may still return true in a destructor that is called during an ordinary return. ~X: called during stack unwinding calls f() void f() { Y y; } // ~Y is called here, uncaught_exception is true

Peter Dimov said: (by the date of Wed, 15 Nov 2006 19:18:22 +0200)
Janek Kozicki wrote:
Peter Dimov said: (by the date of Wed, 15 Nov 2006 15:54:14 +0200)
e.dismiss();
why this? destructor of e does the work. No need to e.dismiss() Dave pointed out that std::uncaught_exception() inside e's destructor can check the conditions upon which the destructor is called and to the necessary work depending on it.
uncaught_exception may still return true in a destructor that is called during an ordinary return.
~X: called during stack unwinding
calls f()
void f() { Y y; } // ~Y is called here, uncaught_exception is true
So I have an idea - maybe put catch{} block inside destructor? :) X::~X() { try { // try nothing. If exception is active, the catch() will get activated :) } catch(boost::exception& e) { e.gotcha(); throw; // wow. It is a throwing destructor. What do you think? } } I haven't tested that, maybe I'm just wrong :> -- Janek Kozicki |

----Original Message---- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Janek Kozicki Sent: 16 November 2006 07:04 To: boost@lists.boost.org Subject: Re: [boost] Review Request: Boost Exception
Peter Dimov said: (by the date of Wed, 15 Nov 2006 19:18:22 +0200)
Janek Kozicki wrote:
Peter Dimov said: (by the date of Wed, 15 Nov 2006 15:54:14 +0200)
e.dismiss();
why this? destructor of e does the work. No need to e.dismiss() Dave pointed out that std::uncaught_exception() inside e's destructor can check the conditions upon which the destructor is called and to the necessary work depending on it.
uncaught_exception may still return true in a destructor that is called during an ordinary return.
~X: called during stack unwinding
calls f()
void f() { Y y; } // ~Y is called here, uncaught_exception is true
So I have an idea - maybe put catch{} block inside destructor? :)
X::~X() { try { // try nothing. If exception is active, the catch() will get // activated :)
This is wrong. The catch will be activated ONLY by exceptions thrown from within the try block. Just because ~X() is being called because of stack unwinding caused by an active exception does NOT mean that the catch block will be entered. ... or did you mean something else?
} catch(boost::exception& e) { e.gotcha(); throw; // wow. It is a throwing destructor. What do you think? } }
I haven't tested that, maybe I'm just wrong :>
-- Martin Bonner Martin.Bonner@Pitechnology.com Pi Technology, Milton Hall, Ely Road, Milton, Cambridge, CB24 6WZ, ENGLAND Tel: +44 (0)1223 203894

Peter Dimov said: (by the date of Wed, 15 Nov 2006 19:18:22 +0200)
Janek Kozicki wrote:
Peter Dimov said: (by the date of Wed, 15 Nov 2006 15:54:14 +0200)
e.dismiss();
why this? destructor of e does the work. No need to e.dismiss() Dave pointed out that std::uncaught_exception() inside e's destructor can check the conditions upon which the destructor is called and to the necessary work depending on it.
uncaught_exception may still return true in a destructor that is called during an ordinary return.
~X: called during stack unwinding
calls f()
void f() { Y y; } // ~Y is called here, uncaught_exception is true
So I have an idea - maybe put catch{} block inside destructor? :)
X::~X() { try { // try nothing. If exception is active, the catch() will get activated :) } catch(boost::exception& e) { e.gotcha(); throw; // wow. It is a throwing destructor. What do you think? } }
I haven't tested that, maybe I'm just wrong :>
-- Janek Kozicki | I do not think it is a good idea to throw exception in the destructor, because the object is destructed, while you throw the exception.The best way to confirm the data is that make sure all the object is release before destruct. I do not know it is the best answer for you!:)
_________________________________________________________________ 与联机的朋友进行交流,请使用 MSN Messenger: http://messenger.msn.com/cn
participants (8)
-
David Abrahams
-
Emil Dotchevski
-
Janek Kozicki
-
Martin Bonner
-
Matthew Herrmann
-
Peter Dimov
-
Sebastian Redl
-
罗 文韬