
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 -