[Exception] Do we really need to register?

I like the append capability of the Exception library I wrote an exception type that packed passed values into a "cons-list" (from the tuple library), and used stream-inserter syntax. The concise syntax has proven to be effective at preventing laziness with error cases. Support for asynchronous exception handling is also very important, but is missing from Exception. Can exception::pimpl remember the user exception type (CRTP), for the sake of clone/raise? The need to preregister tag types is slightly annoying. The need to specify the data-type with the tag is especially annoying. Why not use accompanying text as a key to retrieve values? For example, I would rather write this: class my_exception : public exception<my_exception> {}; void main() { try { int value=5; throw exception() << "Something went wrong. value=" << value; } catch(my_exception& e) { int value=e.get<int>("value="); //search for a field preceded by "value=" cout << value; } } You wouldn't even need to give the key text if you expect the field type to be unique in the exception. The internal map seems awkward, particularly because it discards the order in which attributes are added. -Joshua Napoli

Support for asynchronous exception handling is also very important, but is missing from Exception. Can exception::pimpl remember the user exception type (CRTP), for the sake of clone/raise?
Cloning exceptions is out of the scope of Boost Exception. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html.
The need to preregister tag types is slightly annoying. The need to specify the data-type with the tag is especially annoying.
Why not use accompanying text as a key to retrieve values?
The rationale for the tag type registration is to provide compile-time type safety, which is also why the value type is required at the time of the registration. If there is a consensus that compile-time type safety is less important than the convenience of string-based identification, the interface for adding data to exceptions can be changed: .... catch( boost::exception & x ) { x.add<int>("errno"); throw; } Retrieval would then look like this: .... catch( boost::exception & x ) { if( int const * ec = x.get<int>("errno") ) { //ec points the int at "errno", or... } else { //...is null if x has no such int. } } In my opinion, compile-time type safety is especially important in the case of boost::exception. It is true that type inconsistencies are not the only thing that can go wrong, but the error handling paths of any program are the hardest to test; catching any error-handling bug at compile time is very valuable.
try { int value=5; throw exception() << "Something went wrong. value=" << value; }
This approach is inappropriate in general, because the actual formatting of the text is locale-specific and is best done when handling the exception, not when throwing. Emil Dotchevski

Cloning exceptions is out of the scope of Boost Exception. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html.
I wish that there was a way to get this functionality into boost::exception or boost::thread sooner than 0x. The exception constructor populates a thread-local pointer or something...
In my opinion, compile-time type safety is especially important in the case of boost::exception. [...] the error handling paths of any program are the hardest to test; catching any error-handling bug at compile time is very valuable.
Since there is a possibility that the tag was not used, we get the same number of control paths in the catch block in both the static-type keys and string-based keys. So there probably wouldn't be much difference in testing effort. On the other hand, a visitor interface would eliminate the branch: bool retry(false); do { try { throw my_error() << boost::error_info<tag_errno>(errno); } catch(exception& e) { using namespace boost::lambda; std::string filename("no-name"); //Set retry if we have a tag_errno whose value is EAGAIN //and get the filename from the tag_filename field. e.visit_tags(e , exception::for_tag<tag_errno>(if_then(_1==EAGAIN,var(retry)=true)) ||exception::for_tag<tag_filename>(var(filename)=_1) ); cout<<"There was an error with file named " << filename << ". We will "; if(!retry) cout << "not "; cout<<"retry.\n"; } }while(retry); The (visitor) pattern would apply to string id or description matching style of identifying fields. There is only one obvious behavior for the case where a tag is inserted more than once: the callback is invoked for each value.
throw exception() << "Something went wrong. value=" << value;
This approach is inappropriate in general, because the actual formatting of the text is locale-specific and is best done when handling the exception, not when throwing.
When throwing, the values would be copied (and maybe dynamically allocated, if the type fails has_nothrow_copy), but no formatting applied. Formatting is only applied when exception::what() is called. No matter whether types, string ids or fragments of description strings are used as keys, it is handy to be able to interleave descriptive text with the data fields at the throw site. This is helpful when debugging, since it minimizes the effort required to get descriptive error messages.
participants (2)
-
Emil Dotchevski
-
Josh Napoli