Re: [boost] boost::exception questions

3) Support for linking multiple error tags in a single expression might be useful and help in standarizing errors throughout an application.
throw boost:::exception() << boost::error_info<tag_errorno,tag_filename,tag_size,tag_width>( (1,"output.txt",100,200);
Could you clarify what you mean?
Well, I could do something like this: typedef boost::error_info<tag_errorno, tag_filename,tag_size,tag_width> error_t; throw error_t(1,"output.txt",100,200); If this error was common throughtout an application, this typedef would make the code more readable. Many boost components support variable number of template arguments. It would be trivial to implement I think. You could even default the template arguments, to shorten the expression even further, such as: typedef boost::standard_error<> standard_error_t; throw standard_error_t(); You could do this instead of the "ugly" macros that you have for the "_LINE_", "_FILE_" and "_FUNCTION_".
Usage of the stream operator (<<) for data insertion.
I like using (<<) for data insertion and see no reason to remove it.

On 9/30/07, Tom Brinkman <reportbase@gmail.com> wrote:
3) Support for linking multiple error tags in a single expression might be useful and help in standarizing errors throughout an application.
throw boost:::exception() << boost::error_info<tag_errorno,tag_filename,tag_size,tag_width>( (1,"output.txt",100,200);
Could you clarify what you mean?
Well, I could do something like this:
typedef boost::error_info<tag_errorno, tag_filename,tag_size,tag_width> error_t;
throw error_t(1,"output.txt",100,200);
You probably mean: throw my_error() << error_t(1,"output.txt",100,200); In my opinion what you're describing is better accomplished like this: class my_error: public boost::exception { public: my_error( int errno, std::string file_name, size_t size, int width ) { (*this) << boost::error_info<tag_errno>(errno) << boost::error_info<tag_file_name>(file_name) << boost::error_info<tag_size>(size) << boost::error_info<tag_width>(width); } }; However in my experience with Boost Exception I've never done that. Occasionally I've defined a local function that's called within a given compilation unit, like this: namespace { void throw_my_error( int errno, std::string file_name, size_t size, int width ) { throw my_error() << boost::error_info<tag_errno>(errno) << boost::error_info<tag_file_name>(file_name) << boost::error_info<tag_size>(size) << boost::error_info<tag_width>(width); } } I think it is a mistake to standardize globally on the info you want to stuff into the exception at the time of the throw. What if you detect an error condition that matches the my_error semantics completely, yet you don't have a file name to pass to its constructor? You'd find a way to make the file name available to the throw site, right? Instead, Boost Exception allows you to just throw my_error(), even though you don't have all info needed to handle it; such info can be added later on, as the exception bubbles up to a context where it is available naturally. Similarly, what if you are throwing my_error, but you have additional info that is relevant to the failure? Sure, you could modify the my_error definition to add another constructor, but that's an overkill -- not to mention undesirable because it may force a lot of files to recompile. When I use Boost Exception, the two things I care about are: * Define clear semantics for each exception type * Correctly classify the failure (derive the appropriate bases.) Other than that, all of my exception classes look very much the same: they're empty. This could go into a "best practices" section in the documentation, if we agree that this really is the best approach. Emil Dotchevski

Emil Dotchevski wrote:
On 9/30/07, Tom Brinkman <reportbase@gmail.com> wrote:
3) Support for linking multiple error tags in a single expression might be useful and help in standarizing errors throughout an application.
throw boost:::exception() << boost::error_info<tag_errorno,tag_filename,tag_size,tag_width>( (1,"output.txt",100,200);
Could you clarify what you mean? Well, I could do something like this:
typedef boost::error_info<tag_errorno, tag_filename,tag_size,tag_width> error_t;
throw error_t(1,"output.txt",100,200);
You probably mean:
throw my_error() << error_t(1,"output.txt",100,200);
In my opinion what you're describing is better accomplished like this:
<snip lot's of code> It's not at all obvious why using that much boilerplate is better.
I think it is a mistake to standardize globally on the info you want to stuff into the exception at the time of the throw. What if you detect an error condition that matches the my_error semantics completely, yet you don't have a file name to pass to its constructor? You'd find a way to make the file name available to the throw site, right?
Instead, Boost Exception allows you to just throw my_error(), even though you don't have all info needed to handle it; such info can be added later on, as the exception bubbles up to a context where it is available naturally.
I see. This feature is most valuable. However, so is Tom's suggestion and I don't think both are mutually exclusive (that is if we let go of using the template-id 'error_info' for the latter): typedef boost::custom_exception<tag:: errno,tag::filename,tag::size> file_error; Note custom_exceptions would inherit from boost::exception. We could go further and check the first argument for being an exception type (directly or indirectly derived from boost::exception) to replace that default: typedef boost::custom_exception< an_error , tag::errno,tag::filename,tag::size> file_error; After catching an 'an_error& e' thrown like this throw file_error(errno,name,size); you can still e << boost::error_info<tag::more>(more); Regards, Tobias Schwinger - Review Manager -

<snip> I see. This feature is most valuable. However, so is Tom's suggestion and I don't think both are mutually exclusive (that is if we let go of using the template-id 'error_info' for the latter):
typedef boost::custom_exception<tag:: errno,tag::filename,tag::size> file_error;
Consider this code: void read_file( char const * filename ) { something_unrelated(); .... if( file_failure ) throw file_error() << error_info<tag_filename>(filename); } If I understand correctly, you are concerned with making sure all file_error exceptions pack a file_name. But what if "something_unrelated" throws? Isn't the filename known to our function relevant to that other failure too? It sure is, despite that we have no idea what exceptions "something_unrelated" could throw. 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? Emil Dotchevski

Emil Dotchevski wrote:
<snip> I see. This feature is most valuable. However, so is Tom's suggestion and I don't think both are mutually exclusive (that is if we let go of using the template-id 'error_info' for the latter):
typedef boost::custom_exception<tag:: errno,tag::filename,tag::size> file_error;
Consider this code:
void read_file( char const * filename ) { something_unrelated(); .... if( file_failure ) throw file_error() << error_info<tag_filename>(filename); }
If I understand correctly, you are concerned with making sure all file_error exceptions pack a file_name.
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.
But what if "something_unrelated" throws? Isn't the filename known to our function relevant to that other failure too?
Since it was an abstract example it sure depends on the case.
It sure is, despite that we have no idea what exceptions "something_unrelated" could throw.
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. Of course you're still free to use this pattern at any factorization that is reasonable e.g. providing a 'file_error' base class and an 'io_error' custom_exception that always adds 'errno' and an enum describing the failed operation. Regards, Tobias Schwinger - Review Manager -

<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

Emil Dotchevski wrote:
<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...
<snip>
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.
I agree. But that use case will become even nicer if you don't have to throw the boost::exceptionS yourself as it's done by the components you use, won't it? As you expressed you'd like boost::exception to become THE exception class, I think you should make it as flexible and inviting to use as possible: There is a whole bunch of existing (and stable) code that uses traditional exception classes. Providing means for a smooth transition will probably increase your library's acceptance. Same goes for avoiding boilerplate code and overhead compared to the traditional approach. Please note that my primary intent is not to sell features. I'm basically just repeating a (subjectively useful) suggestion from a reviewer so it does not get wiped off the table before it has been thoroughly investigated. Regards, Tobias Schwinger - Review Manager -

<snip>
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.
I agree. But that use case will become even nicer if you don't have to throw the boost::exceptionS yourself as it's done by the components you use, won't it?
Can you please clarify what do you mean? I do not understand the above paragraph at all...
As you expressed you'd like boost::exception to become THE exception class, I think you should make it as flexible and inviting to use as possible: There is a whole bunch of existing (and stable) code that uses traditional exception classes. Providing means for a smooth transition will probably increase your library's acceptance.
I don't want to get in an argument with my review manager :) but I don't think I've said that I want boost::exception to become THE exception class; it's designed to act as a base class for user-defined exception types much like std::exception is used today. As for integration with existing exception classes, Boost Exception provides a special function called enable_error_info which makes integrating it into existing code base safe and easy: http://www.revergestudios.com/boost-exception/boost-exception.htm#enable_err... Integration in Boost is even easier, as it can be done in a single place, in the boost::throw_exception function.
Same goes for avoiding boilerplate code and overhead compared to the traditional approach.
Could you please explain? How does using Boost Exception require more boilerplate code than the traditional approach? Emil Dotchevski

Emil Dotchevski wrote:
<snip> I agree. But that use case will become even nicer if you don't have to throw the boost::exceptionS yourself as it's done by the components you use, won't it?
Can you please clarify what do you mean? I do not understand the above paragraph at all...
I mean that Exception becomes more useful the more other libraries use it.
As you expressed you'd like boost::exception to become THE exception class, I think you should make it as flexible and inviting to use as possible: There is a whole bunch of existing (and stable) code that uses traditional exception classes. Providing means for a smooth transition will probably increase your library's acceptance.
I don't want to get in an argument with my review manager :) but I don't think I've said that I want boost::exception to become THE exception class; it's designed to act as a base class for user-defined exception types much like std::exception is used today.
OK, then I probably misinterpreted this one: <cite> In my ideal world, the functionality of boost::exception would be implemented directly by std::exception ;) </cite>
As for integration with existing exception classes, Boost Exception provides a special function called enable_error_info which makes integrating it into existing code base safe and easy:
http://www.revergestudios.com/boost-exception/boost-exception.htm#enable_err...
Oh my, I missed it. I should've read this part of the reference docs inspecting the library as that multiple inheritance approach seems highly questionable to me for the following reasons: 1. We can't access the data in the "traditional exception" the Boost.Exception way (it's not tagged), and 2. calling 'what' is probably ambiguous and it seems generally awkward to deal with that exception where (finally) caught. One could argue there is no integration at all as its basically just two independent classes lumped together.
Integration in Boost is even easier, as it can be done in a single place, in the boost::throw_exception function.
While technically possible, this measure requires the authors of throwing libraries to reach consensus that it should happen - and a single developer with objections might blow that plan.
Same goes for avoiding boilerplate code and overhead compared to the traditional approach.
Could you please explain? How does using Boost Exception require more boilerplate code than the traditional approach?
I mean the throw site: throw an_error(a,b,c) vs. throw an_error() << boost::error_info<a_tag>(a) << boost::error_info<b_tag>(b) << boost::error_info<c_tag>(c) Currently it adds both boilerplate code and overhead when porting existing code (in a way so that it throws first-class Boost.Exceptions, that is). Regards, Tobias Schwinger - Review Manager -

Emil Dotchevski wrote:
<snip> I agree. But that use case will become even nicer if you don't have to throw the boost::exceptionS yourself as it's done by the components you use, won't it?
Can you please clarify what do you mean? I do not understand the above paragraph at all...
I mean that Exception becomes more useful the more other libraries use it.
Ah, yes. Which is why adding this library to Boost makes sense to me.
As you expressed you'd like boost::exception to become THE exception class, I think you should make it as flexible and inviting to use as possible: There is a whole bunch of existing (and stable) code that uses traditional exception classes. Providing means for a smooth transition will probably increase your library's acceptance.
I don't want to get in an argument with my review manager :) but I don't think I've said that I want boost::exception to become THE exception class; it's designed to act as a base class for user-defined exception types much like std::exception is used today.
OK, then I probably misinterpreted this one:
<cite>
In my ideal world, the functionality of boost::exception would be implemented directly by std::exception ;)
</cite>
I wouldn't call even std::exception THE exception class; it's not the only class users throw, in fact (much like boost::exception) it can't be thrown directly because it's an abstract class.
As for integration with existing exception classes, Boost Exception provides a special function called enable_error_info which makes integrating it into existing code base safe and easy:
http://www.revergestudios.com/boost-exception/boost-exception.htm#enable_err...
Oh my, I missed it. I should've read this part of the reference docs inspecting the library as that multiple inheritance approach seems highly questionable to me for the following reasons:
1. We can't access the data in the "traditional exception" the Boost.Exception way (it's not tagged), and
This is exactly the same behavior as if you write an exception class that derives from boost::exception and adds its own functionality, which I wouldn't consider to be a problem.
2. calling 'what' is probably ambiguous and it seems generally awkward to deal with that exception where (finally) caught.
The exact type of the object returned by enable_error_info is unspecified; you're not supposed to catch the exception by that type. Of course you can catch it as a boost::exception, in which case what() is not ambiguous. You can also catch it as its original type, in which case what() is also not ambiguous. What do you mean by awkward?
One could argue there is no integration at all as its basically just two independent classes lumped together.
I call this integration, because it instantly enables the boost::exception functionality without breaking user code.
Integration in Boost is even easier, as it can be done in a single place, in the boost::throw_exception function.
While technically possible, this measure requires the authors of throwing libraries to reach consensus that it should happen - and a single developer with objections might blow that plan.
It is my understanding that calling boost::throw_exception when throwing an exception is a requirement, not a guideline. The price of the proposed use of enable_error_info in boost::throw_exception is one empty shared_ptr. In terms of space, this amounts to the size of two pointers added to all exception objects. Do you think that this would be a problem?
Same goes for avoiding boilerplate code and overhead compared to the traditional approach.
Could you please explain? How does using Boost Exception require more boilerplate code than the traditional approach?
I mean the throw site:
throw an_error(a,b,c)
vs.
throw an_error() << boost::error_info<a_tag>(a) << boost::error_info<b_tag>(b) << boost::error_info<c_tag>(c)
When you use boost::exception as a base class for an_error, nothing stops you from writing a constructor which takes a,b and c. Then you can throw an_error(a,b,c) as you would if you didn't use boost::exception. Compared to the traditional approach, I see no difference. I think that we disagree about this issue because without boost::exception, each and every throw needs to provide all info stored in the exception object, which is why it makes sense to write a constructor so that each throw is streamlined. With boost::exception, fewer throws pack the same data anyway. But of course you can write a constructor (as usual) if that's not the case (personally, I prefer using a namespace-scope function to do the same job, as I illustrated in another reply.)
Currently it adds both boilerplate code and overhead when porting existing code (in a way so that it throws first-class Boost.Exceptions, that is).
The easiest way to port existing code to using boost::exception is to use enable_error_info. This adds no boilerplate code, and requires no changes to any exception classes. All you have to do is replace "throw my_exception(a,b,c)" with "throw enable_error_info(my_exception(a,b,c))". Cheers, Emil Dotchevski

On 10/2/07, Emil Dotchevski <emil@revergestudios.com> wrote:
[snip]
The easiest way to port existing code to using boost::exception is to use enable_error_info. This adds no boilerplate code, and requires no changes to any exception classes. All you have to do is replace "throw my_exception(a,b,c)" with "throw enable_error_info(my_exception(a,b,c))".
Has the approach of saving all data to a global structure (instead of to the exception class hierarchy itself) been considered? PS: I'm not suggesting it should be done that way, but only asking if it was considered at all.
Cheers, Emil Dotchevski
Best regards, -- Felipe Magno de Almeida

[snip]
The easiest way to port existing code to using boost::exception is to use enable_error_info. This adds no boilerplate code, and requires no changes to any exception classes. All you have to do is replace "throw my_exception(a,b,c)" with "throw enable_error_info(my_exception(a,b,c))".
Has the approach of saving all data to a global structure (instead of to the exception class hierarchy itself) been considered? PS: I'm not suggesting it should be done that way, but only asking if it was considered at all.
You can not use global storage for the data that's stored in exception objects because in general, multiple exception objects can be active at the same time (this has nothing to do with boost::exception, it's just the way exceptions are defined in C++.) Emil Dotchevski

On 10/2/07, Emil Dotchevski <emil@revergestudios.com> wrote:
[snip]
[snip]
You can not use global storage for the data that's stored in exception objects because in general, multiple exception objects can be active at the same time (this has nothing to do with boost::exception, it's just the way exceptions are defined in C++.)
Can't a std::vector<std::pair<void*, infos> > be used?
Emil Dotchevski
-- Felipe Magno de Almeida

[snip]
[snip]
You can not use global storage for the data that's stored in exception objects because in general, multiple exception objects can be active at the same time (this has nothing to do with boost::exception, it's just the way exceptions are defined in C++.)
Can't a std::vector<std::pair<void*, infos> > be used?
That's a big can of worms... You'd have to maintain the lifetime of the infos manually. Why do you want to make the infos global anyway? After all, their lifetime should be identical to the lifetime of the exception object itself; the easiest way to accomplish this is to use a member of the exception object to store the info. Emil Dotchevski

On 10/2/07, Emil Dotchevski <emil@revergestudios.com> wrote:
[snip]
[snip]
Can't a std::vector<std::pair<void*, infos> > be used?
That's a big can of worms... You'd have to maintain the lifetime of the infos manually.
Yeah, not very pleasant.
Why do you want to make the infos global anyway? After all, their lifetime should be identical to the lifetime of the exception object itself; the easiest way to accomplish this is to use a member of the exception object to store the info.
I'm just thinking that if boost.exception makes use of dynamic allocation, then the naive approach isn't that much different (though a lot worse to implement). But I'll drop these questions, they won't get anywhere.
Emil Dotchevski
Regards, -- Felipe Magno de Almeida

Emil Dotchevski wrote:
Integration in Boost is even easier, as it can be done in a single place, in the boost::throw_exception function. While technically possible, this measure requires the authors of throwing libraries to reach consensus that it should happen - and a single developer with objections might blow that plan.
It is my understanding that calling boost::throw_exception when throwing an exception is a requirement, not a guideline.
The price of the proposed use of enable_error_info in boost::throw_exception is one empty shared_ptr. In terms of space, this amounts to the size of two pointers added to all exception objects. Do you think that this would be a problem?
It also forces a dependency on boost::shared_ptr for all libraries that use boost::throw_exception. boost::shared_ptr in turn has its own dependencies. Although some work seems to have been done on shared_ptr since 1.34. It appears to no longer have a dependency on rtti which was a show stopper for me before. I must have missed when this happened if it was announced. - Michael Marcin

Integration in Boost is even easier, as it can be done in a single place, in the boost::throw_exception function. While technically possible, this measure requires the authors of throwing libraries to reach consensus that it should happen - and a single developer with objections might blow that plan.
It is my understanding that calling boost::throw_exception when throwing an exception is a requirement, not a guideline.
The price of the proposed use of enable_error_info in boost::throw_exception is one empty shared_ptr. In terms of space, this amounts to the size of two pointers added to all exception objects. Do you think that this would be a problem?
It also forces a dependency on boost::shared_ptr for all libraries that use boost::throw_exception. boost::shared_ptr in turn has its own dependencies.
Although some work seems to have been done on shared_ptr since 1.34.
It appears to no longer have a dependency on rtti which was a show stopper for me before. I must have missed when this happened if it was announced.
Right. If the dependency on shared_ptr is problematic, it can be removed as it is strictly an implementation detail. This will also reduce the footprint of class exception to the size of a single pointer. Emil Dotchevski.

Emil Dotchevski wrote:
Oh my, I missed it. I should've read this part of the reference docs inspecting the library as that multiple inheritance approach seems highly questionable to me for the following reasons:
1. We can't access the data in the "traditional exception" the Boost.Exception way (it's not tagged), and
This is exactly the same behavior as if you write an exception class that derives from boost::exception and adds its own functionality, which I wouldn't consider to be a problem.
2. calling 'what' is probably ambiguous and it seems generally awkward to deal with that exception where (finally) caught.
The exact type of the object returned by enable_error_info is unspecified; you're not supposed to catch the exception by that type.
Of course you can catch it as a boost::exception, in which case what() is not ambiguous.
Of course! Stupid me...
You can also catch it as its original type, in which case what() is also not ambiguous.
It doesn't make the design any better, however, as our exception still has two independent interfaces. We can either catch one or the other - user data is not integrated into the boost::exception.
What do you mean by awkward?
Dealing with such a dual interface. Like having to cast between the two to take advantage of the full functionality.
Integration in Boost is even easier, as it can be done in a single place, in the boost::throw_exception function. While technically possible, this measure requires the authors of throwing libraries to reach consensus that it should happen - and a single developer with objections might blow that plan.
It is my understanding that calling boost::throw_exception when throwing an exception is a requirement, not a guideline.
The price of the proposed use of enable_error_info in boost::throw_exception is one empty shared_ptr. In terms of space, this amounts to the size of two pointers added to all exception objects. Do you think that this would be a problem?
No. But design flaws sure wil1. For the reasons pointed out above, I don't believe it's sound to throw an empty boost::exception in this case.
Same goes for avoiding boilerplate code and overhead compared to the traditional approach. Could you please explain? How does using Boost Exception require more boilerplate code than the traditional approach? I mean the throw site:
throw an_error(a,b,c)
vs.
throw an_error() << boost::error_info<a_tag>(a) << boost::error_info<b_tag>(b) << boost::error_info<c_tag>(c)
When you use boost::exception as a base class for an_error, nothing stops you from writing a constructor which takes a,b and c. Then you can throw an_error(a,b,c) as you would if you didn't use boost::exception. Compared to the traditional approach, I see no difference.
This approach provides better integration. But we're back in boilerplate land (as you showed earlier in this thread). Further, it adds so many unnecessary allocator invocations (unnecessary as the library could provide means to avoid them together with the boilerplate code as mentioned earlier) that will be over the top for certain execution environments.
I think that we disagree about this issue because without boost::exception, each and every throw needs to provide all info stored in the exception object, which is why it makes sense to write a constructor so that each throw is streamlined.
With boost::exception, fewer throws pack the same data anyway. But of course you can write a constructor (as usual) if that's not the case (personally, I prefer using a namespace-scope function to do the same job, as I illustrated in another reply.)
Well, there is a lot of existing code that is written the "traditional" way. As mentioned several times, being able to add stuff in outer scopes is most useful. That said, I think it's a mistake to promote it as the only valid use case.
Currently it adds both boilerplate code and overhead when porting existing code (in a way so that it throws first-class Boost.Exceptions, that is).
^^ Bad phrasing, it seems.
The easiest way to port existing code to using boost::exception is to use enable_error_info. This adds no boilerplate code, and requires no changes to any exception classes.
It doesn't make the data in the user exception first-class content of the boost::exception. From my own experience I often found myself defending the review version, coming to realize the vslue of one or the other suggestion afterwards (and implementing it). So I'll just shut up and listen to what else reviewers have to say :-). Regards, Tobias Schwinger - Review Manager -

Tobias Schwinger wrote:
This approach provides better integration. But we're back in boilerplate land (as you showed earlier in this thread).
Boilerplate compared to what? You have to keep in mind where we're coming from: class my_error: public std::exception { private: A a_; B b_; C c_; public: my_error( A a, B b, C c ): a_( a ), b_( b ), c_( c ) { } A a() const { return a_; } B b() const { return b_; } C c() const { return c_; } char const * what() const throw() { // implement what() here - nontrivial } }; Switching to boost::exception actually removes most of the boilerplate code (at the expense of some memory allocations). The dynamic allocation problem can likely be addressed by adding an allocator argument to boost::exception.

Peter Dimov wrote:
Tobias Schwinger wrote:
This approach provides better integration. But we're back in boilerplate land (as you showed earlier in this thread).
Boilerplate compared to what? You have to keep in mind where we're coming from:
<snip code: good old exception class>
Switching to boost::exception actually removes most of the boilerplate code (at the expense of some memory allocations).
Actually I was comparing typedef boost::custom_exception< base,tag_a,tag_b /*...*/> my_exception; and class my_exception : base { public: my_exception(a_t const& a, b_t const& b /*...*/) { (*this) << boost::error_info<tag_a>(a) << boost::error_info<tag_b>(b) // ... } }; . Of course, both variants have the potential to remove quite a lot of boilerplate from traditional exception code.
The dynamic allocation problem can likely be addressed by adding an allocator argument to boost::exception.
Yes, but adding allocator support shouldn't be substitute for considering a more economic data structure (e.g. a single-linked list), IMO. Peter, is there any chance for you to find the time to write a full review? Regards, Tobias Schwinger - Review Manager -

Tobias Schwinger:
Switching to boost::exception actually removes most of the boilerplate code (at the expense of some memory allocations).
Actually I was comparing
typedef boost::custom_exception< base,tag_a,tag_b /*...*/> my_exception;
... This could be a nice addition, but (as far as I can see) it's completely and easily implementable on top of the proposed design.

Peter Dimov wrote:
Tobias Schwinger:
Switching to boost::exception actually removes most of the boilerplate code (at the expense of some memory allocations). Actually I was comparing
typedef boost::custom_exception< base,tag_a,tag_b /*...*/> my_exception;
...
This could be a nice addition, but (as far as I can see) it's completely and easily implementable on top of the proposed design.
Certainly. Now, reassembling the original thought: This kind of interface could be exploited by the library to implement the 'throw e(a,b,c)' case with (almost) no overhead compared to the traditional approach - that is, without having to use a custom allocator. Regards, Tobias Schwinger - Review Manager -

Tobias Schwinger:
typedef boost::custom_exception< base,tag_a,tag_b /*...*/> my_exception;
...
Certainly. Now, reassembling the original thought: This kind of interface could be exploited by the library to implement the 'throw e(a,b,c)' case with (almost) no overhead compared to the traditional approach - that is, without having to use a custom allocator.
Sure, but I'm afraid that this somewhat misses the point. The idea of the proposed exception class is to allow intermediate layers to stuff additional data into the exception as it passes through; data that is not available at the throw point. You could say that only people that actually use this functionality ought to pay for dynamic allocation, and superficially, you'd be right. But layering doesn't quite work that way. In general, the user of the upper layer (who is presumably trying to avoid dynamic allocation) has no control over the middle layer (which is a third-party library). I'm not seeing a practical way to reconcile the "no dynamic allocation" requirement with the current goals of the library. Now that I think of it, a custom allocator won't help much either, as you'd need to pass it down to every low-level component that is throwing exceptions.

On 10/6/07, Peter Dimov <pdimov@pdimov.com> wrote:
Tobias Schwinger:
[snip]
Now that I think of it, a custom allocator won't help much either, as you'd need to pass it down to every low-level component that is throwing exceptions.
Not if the allocator is created in the low-level code and transported as the stack unwinding happens, as I tried to explain in another thread. [snip] -- Felipe Magno de Almeida

Felipe Magno de Almeida:
On 10/6/07, Peter Dimov <pdimov@pdimov.com> wrote:
Tobias Schwinger:
[snip]
Now that I think of it, a custom allocator won't help much either, as you'd need to pass it down to every low-level component that is throwing exceptions.
Not if the allocator is created in the low-level code and transported as the stack unwinding happens, as I tried to explain in another thread.
The low-level code generally has no idea what allocator it needs to create.

On 10/8/07, Peter Dimov <pdimov@pdimov.com> wrote:
Felipe Magno de Almeida:
[snip]
The low-level code generally has no idea what allocator it needs to create.
For boost.exception code only that's not a problem. If we need allocator customization we use the preprocessor. It just can't reuse the allocator for types given from the user. Like container instances. But we can default-construct and swap objects. I know it is not guaranteed not to throw, but most implementations don't. Regards, -- Felipe Magno de Almeida

This could be a nice addition, but (as far as I can see) it's completely and easily implementable on top of the proposed design.
Certainly. Now, reassembling the original thought: This kind of interface could be exploited by the library to implement the 'throw e(a,b,c)' case with (almost) no overhead compared to the traditional approach - that is, without having to use a custom allocator.
I am not sure what you mean by that, since data added to exceptions after the throw has to come from the heap. If you mean that at least data passed to boost::exception at the time of the throw could be embedded into the exception object instead of using separate storage from the heap, that is also not possible (in general) because of the no-throw copy semantics required by the standard. So my question is what do you call "the traditional" approach? Say you want to transport a std::string into an exception. You can't just have: struct my_exception { std::string s_; //may throw on copy! }; What you need is something like: struct my_exception { shared_ptr<std::string> s_; }; This is pretty much what Boost Exception does, except that it's done generically (the current implementation has a bit more overhead because it uses std::map, but this is purely an implementation detail that can be optimized if it ever shows up in someone's profile.) Emil Dotchevski

On 9/30/07, Tom Brinkman <reportbase@gmail.com> wrote:
3) Support for linking multiple error tags in a single expression might be useful and help in standarizing errors throughout an application.
throw boost:::exception() << boost::error_info<tag_errorno,tag_filename,tag_size,tag_width>( (1,"output.txt",100,200);
Could you clarify what you mean?
Well, I could do something like this:
typedef boost::error_info<tag_errorno, tag_filename,tag_size,tag_width> error_t;
throw error_t(1,"output.txt",100,200);
If this error was common throughtout an application, this typedef would make the code more readable.
Tom, I need some more help in understanding what you are proposing. What exactly is error_t? It is confusing because the way you use it above, it appears that it's an exception type. At the same time, it makes reference to boost::error_info, which is _not_ an exception type (in the current definition of Boost Exception, error_info it is not something you throw.) Emil Dotchevski
participants (6)
-
Emil Dotchevski
-
Felipe Magno de Almeida
-
Michael Marcin
-
Peter Dimov
-
Tobias Schwinger
-
Tom Brinkman