Exception: adding diagnostic information from a dtor?
The FAQ for boost exception contain this example snippet for adding
further information to an exception while the call stack is unwound
catch( boost::exception & e )
{
e << foo_info(foo);
throw; //Okay, re-throwing the original exception object.
}
I like that this possible, but I think the resulting code is somewhat
tedious:
void foo(const string& file_name) {
try {
do_something_fallible(file_name);
} catch( boost::exception & e ) {
e << boost::errinfo_file_name(file_name);
throw;
}
}
I'd rather write it like this:
void foo(const string& file_name) {
diagnostic
Michael,
You're right, the boost::current_exception function is only valid
within a try block.
The issue you're raising has to do with the postconditions of
operator<<. Its current semantics guarantee that upon return the error
info is successfully stored in the exception object. This means that
when you catch an exception, you can rely on certain error info to be
present:
catch( file_read_error & e )
{
assert(get_error_info
The FAQ for boost exception contain this example snippet for adding further information to an exception while the call stack is unwound
catch( boost::exception & e ) { e << foo_info(foo); throw; //Okay, re-throwing the original exception object. }
I like that this possible, but I think the resulting code is somewhat tedious:
void foo(const string& file_name) { try { do_something_fallible(file_name); } catch( boost::exception & e ) { e << boost::errinfo_file_name(file_name); throw; } }
I'd rather write it like this:
void foo(const string& file_name) { diagnostic
(file_name); do_something_fallible(file_name); } My first, non-generic attempt fails:
class diagnostic { public: explicit diagnostic(const string& file_name) : file_name_(file_name) {} ~diagnostic() { if ( boost::exception_ptr e = boost::current_exception() ) { *e << boost::errinfo_file_name(file_name_.c_str()); } } private: const string file_name_; };
The requirements for boost::current_exception() state that it may only be called inside a catch block, therefore the code above is illegal to begin with. Ignoring that, operator<< doesn't appear to be suitably overloaded for this use, anyway.
Now, I'm wondering, is what I'm trying to achieve a bad idea? If not, is there another way to do it?
Michael
-- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/ _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On Wednesday 15 June 2011, Emil Dotchevski wrote:
Michael,
You're right, the boost::current_exception function is only valid within a try block.
The issue you're raising has to do with the postconditions of operator<<. Its current semantics guarantee that upon return the error info is successfully stored in the exception object. This means that when you catch an exception, you can rely on certain error info to be present:
catch( file_read_error & e ) { assert(get_error_info
(e)!=0); std::cerr << "Error reading " << *get_error_info (e); } Without this guarantee, the catch site would be required to deal with any exception even if it has no error info in it.
Emil, thanks for your explanation. Unfortunately, I don't understand it. I do understand that e << errinfo_file_name(fn), if successful at all (i.e., it doesn't throw itself) guarantees that the respective info is present in e. However, this is a dynamic property, I don't see any static reason why it must be true for every file_read_error. So, in principle, a catch site for that exception would have to take into account that this info might be missing. Am I missing something?
If you are to add error info to exception objects in destructors, this guarantee can not hold.
I'm not sure I understand this either. If file_read_error can be thrown without initially having an errinfo_file_name and this info is only added during unwinding higher up the call stack, then why does it make a difference if it is in a catch block or in a destructor (ignoring for a moment that calling current_exception isn't legal there)? Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
On Tue, Jun 14, 2011 at 4:00 PM, Michael Schuerig
On Wednesday 15 June 2011, Emil Dotchevski wrote:
Michael,
You're right, the boost::current_exception function is only valid within a try block.
The issue you're raising has to do with the postconditions of operator<<. Its current semantics guarantee that upon return the error info is successfully stored in the exception object. This means that when you catch an exception, you can rely on certain error info to be present:
catch( file_read_error & e ) { assert(get_error_info
(e)!=0); std::cerr << "Error reading " << *get_error_info (e); } Without this guarantee, the catch site would be required to deal with any exception even if it has no error info in it.
Emil,
thanks for your explanation. Unfortunately, I don't understand it. I do understand that e << errinfo_file_name(fn), if successful at all (i.e., it doesn't throw itself) guarantees that the respective info is present in e. However, this is a dynamic property, I don't see any static reason why it must be true for every file_read_error. So, in principle, a catch site for that exception would have to take into account that this info might be missing. Am I missing something?
The postcondition in op<< allows the programmer to make a design decision that any and all file_read_error exceptions reaching a particular catch site in the program will be guaranteed to have a file name in them, and to treat the absence of a file name as a logic error (that is, a bug, hence the assert in my example.) This is similar to passing something to an exception type's constructor: it'll throw (something else) if it failed to construct, right? The semantics of op<< simply extend this behavior to the case when you stuff something in the exception object at a later time. I'm not implying that it is always invalid to have optional error info in exception objects, only that without this guarantee you couldn't make error info non-optional.
If you are to add error info to exception objects in destructors, this guarantee can not hold.
I'm not sure I understand this either. If file_read_error can be thrown without initially having an errinfo_file_name and this info is only added during unwinding higher up the call stack, then why does it make a difference if it is in a catch block or in a destructor (ignoring for a moment that calling current_exception isn't legal there)?
You're right that it doesn't make a difference, provided that you don't choose to treat the absence of certain error info as a bug. The point is that if you always add error info in destructors, that choice is not available and you must be able to deal with any exception only knowing its type. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Wednesday 15 June 2011, Emil Dotchevski wrote:
On Tue, Jun 14, 2011 at 4:00 PM, Michael Schuerig
wrote: On Wednesday 15 June 2011, Emil Dotchevski wrote:
Michael,
You're right, the boost::current_exception function is only valid within a try block.
The issue you're raising has to do with the postconditions of operator<<. Its current semantics guarantee that upon return the error info is successfully stored in the exception object. This means that when you catch an exception, you can rely on certain error info to be present:
catch( file_read_error & e ) { assert(get_error_info
(e)!=0); std::cerr << "Error reading " << *get_error_info (e); } Without this guarantee, the catch site would be required to deal with any exception even if it has no error info in it.
Emil,
thanks for your explanation. Unfortunately, I don't understand it. I do understand that e << errinfo_file_name(fn), if successful at all (i.e., it doesn't throw itself) guarantees that the respective info is present in e. However, this is a dynamic property, I don't see any static reason why it must be true for every file_read_error. So, in principle, a catch site for that exception would have to take into account that this info might be missing. Am I missing something?
The postcondition in op<< allows the programmer to make a design decision that any and all file_read_error exceptions reaching a particular catch site in the program will be guaranteed to have a file name in them, and to treat the absence of a file name as a logic error (that is, a bug, hence the assert in my example.)
This is similar to passing something to an exception type's constructor: it'll throw (something else) if it failed to construct, right? The semantics of op<< simply extend this behavior to the case when you stuff something in the exception object at a later time.
Is this a correct paraphrase: op<< either adds additional info to an exception or it raises another exception indicating its failure.
If you are to add error info to exception objects in destructors, this guarantee can not hold.
I'm not sure I understand this either. If file_read_error can be thrown without initially having an errinfo_file_name and this info is only added during unwinding higher up the call stack, then why does it make a difference if it is in a catch block or in a destructor (ignoring for a moment that calling current_exception isn't legal there)?
You're right that it doesn't make a difference, provided that you don't choose to treat the absence of certain error info as a bug. The point is that if you always add error info in destructors, that choice is not available and you must be able to deal with any exception only knowing its type.
Let's see. When I catch one exception and while I add information to it, a new exception is thrown, it is this new exception that will propagate up the call stack. By contrast, when a destructor is called due to an exception and the code in the destructor does something that throws another exception, this causes program termination. -- Is that it? Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
On Wed, Jun 15, 2011 at 1:08 AM, Michael Schuerig
Is this a correct paraphrase: op<< either adds additional info to an exception or it raises another exception indicating its failure.
Yes. That way, if you get the original exception, it is guaranteed to have the information needed to handle it; and in case op<< throws, you'll get another (presumably more severe) exception.
Let's see. When I catch one exception and while I add information to it, a new exception is thrown, it is this new exception that will propagate up the call stack. By contrast, when a destructor is called due to an exception and the code in the destructor does something that throws another exception, this causes program termination. -- Is that it?
I was assuming that program termination would be unacceptable, so the destructor would have to catch and eat exceptions that op<< emits, and this means that the catch site could be getting exceptions that are missing random information, up to possibly not having any. So, it has to be able to deal with any exception knowing *only* it's type. Like I said, I'm not against this functionality being available, but it isn't going to be trivial to implement. Essentially, when an exception is created, a pointer allocated in thread-local storage needs to be initialized to point to it so that destructors can access it. However, you can have multiple active exceptions, so what is really needed is a stack of pointers, allocated in thread-local storage. And it has to be portable. :) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Jun 14, 2:45 pm, Michael Schuerig
void foo(const string& file_name) { try { do_something_fallible(file_name); } catch( boost::exception & e ) { e << boost::errinfo_file_name(file_name); throw; }
}
I'd rather write it like this:
void foo(const string& file_name) { diagnostic
(file_name); do_something_fallible(file_name); }
sorry if I don't get the question right. can't you just do this?
} catch( boost::exception & e ) { throw (e << boost::errinfo_file_name(file_name) ); }
On Wed, Jun 15, 2011 at 7:02 PM, alfC
sorry if I don't get the question right. can't you just do this?
} catch( boost::exception & e ) { throw (e << boost::errinfo_file_name(file_name) ); }
Unless there is a bug in Boost Exception, the above should give you a compile error. By design, to avoid slicing, the boost::exception type is an abstract base type and you can not throw it. The correct syntax would be: e << boost::errinfno_file_name(file_name); throw; //OK, throws the *original* exception object. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
participants (3)
-
alfC
-
Emil Dotchevski
-
Michael Schuerig