Boost logo

Boost :

Subject: Re: [boost] Boost and exceptions
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2012-06-23 15:10:48


AMDG

On 06/23/2012 12:11 PM, Robert Ramey wrote:
>
> I also used boost::throw_exception in order to permit the system
> to gracefully handle the case where exception were either not
> available or not permited in the user environment. boost::throw_exception
> was explicilty designed for this purpose and no other.
>

This is the basic point of contention.

According to those who favor the new version,
boost::throw_exception is a hook to control the
behavior of exceptions thrown by Boost libraries, and
I think this is a perfectly reasonable interpretation.
The original behavior was limited to two options:

a) throw e;
b) User defined boost::throw_exception(std::exception&)

Boost.Exception added a third option:
c) throw enable_current_exception(enable_error_info(x))

The important thing is that adding (c) didn't
change the /interface/ of boost::throw_exception.

> So - boost::throw_except is changed and.... what? I already included
> all the useful information I can add. How can boost::throw_exception
> add to this on it's own?

It can't. Boost.Exception allows /callers/
of your library to add their own information
to the exception if they have more context
than the Serialization library itself does.

>> However, if all thrown exceptions inherit from boost::exception, then
>> you simply catch that exception type, ask it to clone itself, and
>> rethrow in a different thread. You preserve all the original error
>> information, and crucially, all the original type information, even
>> though the future doesn't itself know what the actual dynamic type of
>> the caught and rethrown exception is.
>
> Hmmm - I don't see how just changing the implementation of
> boost::throw_exception would change this in anyway. Perhaps you're
> suggesting that in addition to chaning the functionality of boost::throw
> the serialization libary should redefine archive_exception to derive
> from boost::exception rather directly from std::exception. Of course
> that's a totally different topic than we've been discussing up until now.
> and I guess would be a new thread. But maybe you're not suggesting
> that. Sorry if I got this wrong.
>

Actually, inheriting from boost::exception doesn't
work for this. To enable copying you have to throw
using boost::enable_current_exception.

try {
  throw enable_current_exception(x);
} catch(...) {
  exception_ptr e = current_exception();
}
// move e to another thread
rethrow_exception(e);

> Still, you're question obligated me to think a little about this. I would
> expect a library such as boost exception would have code along
> the following lines somewhere to be included/invoked from user
> code.
>
> void * x
> x = dynamic_cast<boost::exception be *>(std::current_exception())
> if(0 != x){
> ... // do great boost exception stuff
> }
> else {
> void * x = dynamic_cast<std::exception be *>(std::current_exception());
> .. // use what() and do some stuff
> }
> else{
> // handle the case where the library, user didn't bother to use
> std::exception
> }
>
> But this is totally compatible with the original implementation of
> boost::throw_exception!
>

Sure, but it also has nothing in particular
to do with what Boost.Exception provides.

> So it looks to me that changing the functionality of boost::throw adds
> nothing
> to libraries which don't use boost::exception.
>

You're looking at this the wrong way. It isn't
necessarily the /library/ that uses Boost.Exception
functionality. Whether Boost.Exception is useful
depends on the users of the library. As such,
it seems reasonable to allow users to decide whether
they want to use Boost.Exception, which is exactly
what happens if the library uses the current
incarnation of boost::throw_exception.

> I still don't see how boost exception helps in my example. If you don't care
> about handling archive_exception you can just use
>
> ifsteam is("my file")
> binary_iarchive ia(is);
> my_data d;
> ia >> d;
>
> and just handle std::exception upstream.

The code using Boost.Exception would look like:

typedef boost::error_info<
  struct tag_archive_filename, std::string> archive_filename;
...

try {
  ifstream is(filename);
  binary_archive ia(is);
  my_data d;
  ia >> d;
} catch (boost::exception& e) {
  e << archive_filename(filename);
  throw;
}

> If you really like
> boost::exception
> your code when you eventually can eventually rethrow
>
> catch (std::exception e){
> BOOST_EXCEPTION_THROW_EXCEPTION(e)
> }
>

This of course loses the original type of the exception.

>> Now consider that the serialization code includes some use of
>> Boost.Filesystem, Boost.Regex (filename parsing) and heaven known
>> whatever other dependencies the object being [de]serialized depends
>> on (presumably an object being deserialised could throw any exception
>> Boost is capable of). If all those exception objects uniformly derive from
>> boost::exception
>> somewhere in their object hierarchy, then you catch *one* exception
>> type, annotate it, and rethrow. All the original type information is
>> retained, clients can catch the boost::exception and enumerate all
>> the information it holds, or they can catch something more specific
>> like boost::archive::exception and work from there.
>
> How is this different from using std::exception ?
>

std::exception doesn't allow any addition annotation.
Using std::exception, there's simply no way to
add any additional information without either
a) Knowing the type of the original exception, or
b) Discarding all the type information of the original exception.

In Christ,
Steven Watanabe


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk