Boost logo

Boost :

From: Emil Dotchevski (emil_at_[hidden])
Date: 2007-10-01 14:19:55


> <snip>
> No, not my point. It's about increasing the expressiveness of client
> code: It provides means for a user to express: "I'm going to throw a
> bunch of exceptions (starting) with common attributes. Please give me a
> brief way to create them, so error reporting doesn't obscure my code."
>
> It also allows to easily port "traditional" exception classes to
> boost::exception.
>
> Further it would allow to allocate the attributes of a custom_exception
> in one shot.

Yes, I think I understand your point, but...

> > So, I'd write that function like this:
> >
> > void read_file( char const * filename )
> > {
> > try
> > {
> > something_unrelated();
> > ....
> > if( file_failure )
> > throw file_error();
> > }
> > catch( boost::exception & x )
> > {
> > x << error_info<tag_filename>(filename);
> > throw;
> > }
> > }
> >
> > How would your custom_exception idea fit in this framework?
>
> So now you're bringing up a different use case.

... I'm bringing up the different use case because -- at least in my
experience -- it is very common when using boost::exception; I'm
trying to convince the audience that the effort to bundle multiple
pieces of data together perhaps isn't as beneficial as one hopes,
because a lot of times all you have is a single piece of data that is
relevant to a failure.

Please bear with me for one more example :)

shared_ptr<FILE> open_file( char const * name, char const * mode )
{
    if( FILE * f=fopen(name,mode) )
        return shared_ptr<FILE>(f,fclose);
    throw file_open_error() <<
        error_info<tag_file_name>(name) <<
        error_info<tag_open_mode>(mode) <<
        error_info<tag_errno>(errno);
}

void read_file( shared_ptr<FILE> const & f, std::vector<char> & buf )
{
    ....
    size_t s=file_size(f);
    if( s>max_size )
        throw file_too_big_error() <<
            error_info<tag_file_size>(s) <<
            error_info<tag_max_file_size>(max_size);
    ....
}

void parse_data( char const * data, size_t s )
{
    ....
    if( parse error )
        throw parse_data_error() <<
            error_info<tag_parse_error_code>(15) <<
            error_info<tag_parse_error_offset>(offset) <<
            erorr_info<tag_parse_buffer>(std::string(data,s));
    ....
}

void parse_file( char const * name )
{
    shared_ptr<FILE> f=open_file(name,"rt");
    try
    {
        std::vector<char> buf;
        read_file(f,buf);
        parse_data(&buf[0],buf.size());
    }
    catch( boost::exception & x )
    {
        x << error_info<tag_filename>(name);
        throw;
    }
}

Though this code is far from being complete, parse_file can emit the
following exceptions already:

file_open_error
file_too_big_error
parse_data_error

If we make this example complete, the list will be longer still.

In my mind, it makes no sense to bundle tag_file_name, tag_open_mode,
and tag_error together (which, if I understand is what you were
thinking), because they appear together in only one exception:
file_open_error.

On the other hand, tag_file_name is relevant to any failure occuring
within parse_file. We shouldn't worry about what else might be
relevant, or how to bundle it -- we just stuff in the exception
whatever is known to us. In particular, note that parse_data doesn't
deal with files at all; it can't possibly "bundle" a file name in the
parse_data_error exception, but at the same time if the data came from
a file, we don't ever want that particular parse_data_error to reach a
catch without a file name.

Emil Dotchevski


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