Boost logo

Boost :

From: Emil Dotchevski (emil_at_[hidden])
Date: 2007-10-23 15:00:14


On 10/23/07, Alexander Nasonov <alnsn_at_[hidden]> wrote:
> Your exception class is not a replacement for std::exception. You recommend
> deriving from your class _and_ std::exception.
> <snip>
> It has no is-a relation with std::exception. So, it's more like
> "addition" rather than "extension".

In principle, there is no problem for boost::exception to derive from
std::exception, or even to replace std::exception.

In practice, we have legacy code that derives from std::exception
already, and making boost::exception not derive from std::exception
enables integration with existing 3rd party and standard code through
enable_error_info.

Do you see any practical problems with this design decision?

> > You don't wrap standard exceptions with correspondent std::exception, you
> > just derive std::exception. What do you mean by "wrapping"?
>
> That's what I mean:
>
> catch(std::bad_argument const& ex)
> {
> throw boost::bad_argument(ex);
> }
> ... and so on for every std exception.

You probably don't want to replace std::bad_argument with some
boost::bad_argument because you would break standard-complying code
that expects to catch std::bad_argument.

That said, boost::exception does provide integration with the standard
exception classes.

In your own code you can change a "throw std::bad_argument()" to
"throw boost::enable_error_info(std::bad_argument())", which will not
break any existing code that catches std::bad_argument, yet will
enable newer, Boost Exception-aware code to intercept the exception
and add information.

If the std::bad_argument exception originated from the standard
library or some 3rd-party code, then you can catch and throw, like
this:

catch(std::bad_argument const& ex)
{
    throw boost::enable_error_info(ex);
}

More information is available here:

http://www.revergestudios.com/boost-exception/boost-exception.htm#existing_hierarchies

> If you caught an exception at upper layer, it'd be extremely useful to
> iterate over all tags at lower layers without maintaining a list of tags.
> One strategy here is to create a hierarchy of tags with lower-level tags
> derived from upper-level tags and have a method that returns a container
> of tags:
>
> vector<ThisLayerTag*> infos = get_derived_error_info<ThisLayerTag>(ex);
>
> or something like that with nicer interface.

Do you have a less abstract, real-life use case for this?

In particular, the reason Boost Exception lacks iteration over the
stored tags is that after more than a year of using it in my own code
base, I never needed to iterate.

In general, the rationale for not supporting iteration is that
iteration presumes catching boost::exception and then probing it for
the stored information in an attempt to try to understand what went
wrong.

A better strategy is to catch different types for different failures,
and then (knowing the semantics of each exception type) get the
information you know is available in the exception object.

> > Stack trace can not be implemented in a platform independent manner;
> > we need the collaboration of the Boost community to implement it
> > correctly on multiple platforms. It is a debugging feature that once
> > implemented, will appear automatically in the ::what() message.
>
> OK, I'll call it "catch points" or "trace points" then. User push_back
> something in every catch clause and reshow. Unlike tags, which have
> unique types, this stack stores values of one type. For example,
>
> catch( my_error& e)
> {
> e.push_back(catch_point(__FILE__, __LINE__));
> throw;
> }
>
> where catch_point is std::pair<char const*, int> or a struct.

This can be easily implemented on top of the proposed design:

typedef std::pair<char const *,int> catch_point;
typedef std::list<catch_point> catch_points;
struct tag_catch_points: boost::error_info<catch_points> { };

void add_catch_point( boost::exception & e, char const * file, int line )
{
    if( !boost::get_error_info<tag_catch_points>(e) )
        e << boost::error_info<tag_catch_points>(catch_points());
    (*boost::get_error_info<tag_catch_points>(e)).push_back(catch_point(file,line));
}

and then:

catch( my_error& e)
{
    add_catch_point(e,__FILE__,__LINE__);
    throw;
}

> <snip>
> std::exception designed with this in mind because what() returns
> char const* and has no-throw guarantee. Your library should follow
> this principle.

boost::exception::what() also returns char const * and has to-throw
guarantee. It follows all standard requirements for exception classes.

> I'd like to repeat it. It's very important to avoid throwing exceptions
> implicitly inside a catch clause.

I see no problem for the code inside a catch to throw exceptions.

Emil Dotchevski


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