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<errinfo_file_name>(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:mic...@schuerig.de
http://www.schuerig.de/michael/
_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users
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<errinfo_file_name>(e)!=0);
std::cerr << "Error reading " << *get_error_info<errinfo_file_name>(e);
}
Without this guarantee, the catch site would be required to deal with
any exception even if it has no error info in it.
If you are to add error info to exception objects in destructors, this
guarantee can not hold. So it seems that even though it is less
convenient and more explicit, the catch( boost::exception & e ) { e <<
errinfo_file_name(n); throw; } approach is preferable.
That said, I am not against implementing a generic type that adds
error info in its destructor as you and others have suggested, as long
as it is kept separate and doesn't change the current op<<
postconditions. Implementing this functionality in a portable manner
is not trivial, but if you or someone else wants to tackle it, I can
provide some support.
Emil Dotchevski
Reverge Studios, Inc.
http://www.revergestudios.com/reblog/index.php?n=ReCode
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)?
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
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:mic...@schuerig.de
http://www.schuerig.de/michael/
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 <michael.li...@schuerig.de>
wrote:
> 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<errinfo_file_name>(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) );
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