[review] Exception - review period extended

The review period of the proposed Exception library has been extended until October 20th. Although several people seemed interested in the submission, we only have two votes, so far. So we really need more votes. Further, I'm concerned about some potential issues, which I wouldn't want to bless into Boost without being sure there has been a thorough investigation (see below). So, I want to encourage you to evaluate the submission, in particular regarding the following questions: 1. Should the library be accepted as a Boost library? For a guideline on what to include in your evaluation see http://www.boost.org/more/formal_review_process.htm 2. Is the design of the 'enable_error_info' function (see http://www.revergestudios.com/boost-exception/boost-exception.htm#existing_h... and http://www.revergestudios.com/boost-exception/boost-exception.htm#enable_err... ), using multiple inheritance, really sound? 3. Does the current implementation use appropriate data structures? We are looking forward to your reviews! Regards, Tobias Schwinger - Review Manager -

On Fri, 12 Oct 2007, Tobias Schwinger wrote:
The review period of the proposed Exception library has been extended until October 20th.
1. Should the library be accepted as a Boost library? For a guideline on what to include in your evaluation see
I did not use or studied this library (as well as did not use other Boost libraries and not planning to use either of them including this one), so it would be unfair for me to vote for or against this particular library, but just reading mails arriving from this list and having some experience in exception handling I am wordering why in the list of questions for reviewers there is no question about whether the name of the studied library corresponds to its essense, or it is too wide or biased. If to continue this thought, there may be many other important questions missed. Regards Igor Smirnov

<snip> I am wordering why in the list of questions for reviewers there is no question about whether the name of the studied library corresponds to its essense, or it is too wide or biased.
The library name issue was raised in this discussion, as well as in the preliminary discussions. Boost Exception would be most useful if it is adopted by a wide user base, as that would allow mid-level contexts to intercept any exception and augment it with additional data without specific knowledge of lower level libraries that throw exceptions, and higher level libraries that handle them. The class name boost::exception was chosen to represent its intended purpose as a base class for all exception types, much like std::exception. It has been carefully designed to be compatible with the current semantics of std::exception; this enables a future revision of the C++ standard to expand std::exception to include similar data-transport functionality. Emil Dotchevski

Emil Dotchevski wrote:
<snip> I am wordering why in the list of questions for reviewers there is no question about whether the name of the studied library corresponds to its essense, or it is too wide or biased.
Where does this quote come from? Seems I missed that message... Oh BTW, did you notice the reviews (one posted to the first review thread and the other one to the user's list http://permalink.gmane.org/gmane.comp.lib.boost.devel/166104 http://permalink.gmane.org/gmane.comp.lib.boost.user/30965 )?
The library name issue was raised in this discussion, as well as in the preliminary discussions.
Boost Exception would be most useful if it is adopted by a wide user base, as that would allow mid-level contexts to intercept any exception and augment it with additional data without specific knowledge of lower level libraries that throw exceptions, and higher level libraries that handle them.
The class name boost::exception was chosen to represent its intended purpose as a base class for all exception types, much like std::exception. It has been carefully designed to be compatible with the current semantics of std::exception; this enables a future revision of the C++ standard to expand std::exception to include similar data-transport functionality.
So it sounds there is no name issue and as if boost::exception should become a std::exception -- one way or the other... Regards, Tobias Schwinger - Review Manager -

I think the library should be included in Boost because ... well, I use it and found it very convenient in my code. ------------------------------- There are several situation where this library may come very handy (expected use cases should be mentioned in the docs, btw): 1. As a replacement of ad-hoc and messy network of exception classes with something less complicated (this is why I use it). This, in turn, makes unit tests more stable and thus easier to write. 2. As a unified solution for large systems where user codes the lowest level bits, the middle level is provided as a framework and highest part is again coded by the user. Without solution like boost::exception one would need to accomodate with exceptions to the framework, not the other way around. Here, the framework would not dictate what to do. 3. The library has a potential to deal with exception handling in a structured way. Say there's low level module A, middle level module B and high level module C. Module B uses A, C uses B. Some tool can be implemented that processes or converts all exceptions thrown from A *inside* B so module C will only see interface exposed from B and never ever anything from A. This library would allow to implement such handling in a single place, to implement automated checking and would allow to output detailed information in case of programmer mistake. It should eliminate need for chaining exceptions which still exposes lower level details It would be useful for this purpose to provide an ability to iterate through the values or ability to pass a visitor or both. ------------------------------- Notes about the code: 1. exception/exception.hpp (btw, this file name can easily confuse one with the same filename one level down) - assert() may be replaced with BOOST_ASSERT or even better commented out. People may use their own assert implementation and this would make the exception module (something what should be really the core part) dependent on something else. 2. shared_ptr<> may be replaced by intrusive_ptr<> or home made equivalent. This (a) reduces dependency on other Boost part (shared_ptr does depend on quite few other libs) and (b) the atomic locking used by shared_ptr would be eliminated. This would help a little bit on multiprocessor systems - lock may be hundredths of cycles and during the time access to the memory bus is disabled. 3. The part of code in exceptions/exception.hpp that may be put into separate TU could be wrapped in #ifdef BOOST_EXCEPTION_THIS_IS_INSERTED_INTO_SEPARATE_CPP_FILE ... #endif Someone may have Boost as read-only files and/or be using SVN version and unwilling to do it again and again. 4. The boost/exception.hpp may have #if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif to help a tiny bit with compilation times. ------------------------------- Notes about the docs: 1. Section Logging may provide examples how to implement pretty-printing of the user data. 2. The documentation may say whether it is possible to use multiple inheritance with boost::exception descendants and show how. I tried it some time ago, it didn't compile so I gave up. 3. The example using existing exception class hierarchy should be longer and show how one can catch such exception. It may be obvious but it's better documented explicitly. 4. The example and the tests from libs/exception may be linked with short narrative what one can see here. There's never enough of examples. ------------------------------- Other possible features: 1. As mentioned above, the ability to iterate through values and/or pass a visitor. 2. Cloning of the exception. When I catch exception thrown in a worker thread I create clone of it and throw it in the thread which requested the task to be executed in separate thread. It looks like: struct my_exc : public boost::exception { virtual my_exc* clone() { return new *this; } }; I'd created common abstract subclass because of it, perhaps it would make sense somehow to support the feature. I see the http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html now but it doesn't help me here and now, I think. 3. My code often looks like: throw exc::some_exception() << boost::error_info<exc::tags::my_tag1>(errno) << boost::error_info<exc::tags::my_tag2>("this") << boost::error_info<exc::tags::my_tag3>("that") ; IOW, very long and hard to read lines. I would welcome some way to fill in the exception in more compact form. I, however, do not have any suggestion how to do it. 4. I would like the ability to collect traces generated by what() function in DEBUG mode, something as: catch (my_low_level_exception& e) { my_high_level_exception e2; ... fill in e2 e2.add_debug_trace(e); // adds e.what() somewhere, no-op in release mode throw e2; } ... catch (my_high_level_exception& e) { cout << e.dump_collected_debug_trace(); // no-op in release } This would print complete debug information yet not expose any low level details. ------------------------------- Some time ago (Aug 17, 2007, in an ancient thread "Review Request: Boost Exception") Daryle Walker asked few questions about the library: 1. What happens if a "boost::error_info" tag is used multiple times on the same object? Is it an error (exception or assert?), do the instances get combined like a multi-set, or do all instances besides the first (or last) get ignored/dropped? Shouldn't this be documented? 2. Shouldn't all inheritance of "boost::exception," especially via "enable_error_info," be of the virtual kind? Catching a given class type only works if it has exactly one connection to the actual exception object, which means that: a. The type is the final type of the object b. The type appears exactly once in the inheritance hierarchy c. All inheritance appearances of the type are virtual If a class type uses another type as a base multiple times, and at least one appearance is _not_ virtual, then catches with that base will be skipped when that first type is thrown. Imagine what happens if "enable_error_info" is applied multiple times during an exception session and/or the original exception object used "boost::exception". ------------------------------- About the compile safety of tags: I prefere to have them statically checked. Acesibility of the tags could be limited only for relevant parts of an application thus removing chance to depend on such internal details in urelated code. String based indentification, if used, should be an option, not mandatory. ------------------------------- It would be ideal, if the library is accepted into Boost that all currently thrown exception from Boost libraries (e.g. archive_exception from Serialization) descend from boost::exception. (I am bot holding my breath, this is just an unreachable ideal.) /Pavel

Pavel, First of all, thanks for the detailed review.
1. exception/exception.hpp (btw, this file name can easily confuse one with the same filename one level down) - assert() may be replaced with BOOST_ASSERT or even better commented out. People may use their own assert implementation and this would make the exception module (something what should be really the core part) dependent on something else.
If there is a requirement for boost libraries to use BOOST_ASSERT, I'll make that change if the library is officially accepted in Boost.
2. shared_ptr<> may be replaced by intrusive_ptr<> or home made equivalent. This (a) reduces dependency on other Boost part (shared_ptr does depend on quite few other libs) and (b) the atomic locking used by shared_ptr would be eliminated. This would help a little bit on multiprocessor systems - lock may be hundredths of cycles and during the time access to the memory bus is disabled.
(a) is a valid concern, OTOH shared_ptr is such a low level component of Boost that -- as careful as I am in avoiding physical coupling -- I don't consider it a real dependency; rather, it's a tool for avoiding dependencies. (b) can be addressed if someone reports having performance issues with Boost Exception. This is highly unlikely since we're comparing the time it takes to copy a single shared_ptr once (at the time of the initial throw) vs. the time it takes for the implementation to unroll the stack until a suitable catch is found.
2. The documentation may say whether it is possible to use multiple inheritance with boost::exception descendants and show how. I tried it some time ago, it didn't compile so I gave up.
With this regard, boost::exception is basically the same as std::exception, which is why I didn't bother to elaborate on how to use it as a base class, but at least I should point out these similarities in the documentation; good point.
1. As mentioned above, the ability to iterate through values and/or pass a visitor.
Iteration presumes that you catch a boost::exception knowing nothing about the semantics of the actual exception type that was thrown, yet you want to make use of the data it contains. One use case would be to log all data contained in an (unknown) exception, for debugging purposes; this is supported by boost::exception::what(). Another use case is when you want to catch exception type A and throw exception type B instead, such that the B object contains all the data from the A object plus some additional information. However, one of the motivations for Boost Exception is that it helps avoid the need to translate exception types, since you can catch A as boost::exception &, add whatever relevant data you have, and then re-throw the original A object.
2. Cloning of the exception. When I catch exception thrown in a worker thread I create clone of it and throw it in the thread which requested the task to be executed in separate thread. It looks like:
struct my_exc : public boost::exception { virtual my_exc* clone() { return new *this; } };
I'd created common abstract subclass because of it, perhaps it would make sense somehow to support the feature.
I see the http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html now but it doesn't help me here and now, I think.
I believe that cloning exceptions is outside the scope of Boost Exception.
3. My code often looks like:
throw exc::some_exception() << boost::error_info<exc::tags::my_tag1>(errno) << boost::error_info<exc::tags::my_tag2>("this") << boost::error_info<exc::tags::my_tag3>("that") ;
IOW, very long and hard to read lines. I would welcome some way to fill in the exception in more compact form. I, however, do not have any suggestion how to do it.
There was a suggestion to support grouping of separate data that is always added together to a boost::exception at the time of the throw. As Peter pointed out, this support can be implemented on top of the proposed design. Another possibility -- which would be my choice -- is to simply put such data into a struct that gets added to boost::exception as a single object.
4. I would like the ability to collect traces generated by what() function in DEBUG mode, something as:
catch (my_low_level_exception& e) { my_high_level_exception e2; ... fill in e2 e2.add_debug_trace(e); // adds e.what() somewhere, no-op in release mode throw e2; }
Would this work for you: struct tag_debug_trace: boost::error_info<std::string> { }; void add_debug_trace( boost::exception & e2, boost::exception & e ) { #ifdef _DEBUG if( !boost::get_error_info<tag_debug_trace>(e2) ) e2 << boost::error_info<tag_debug_trace>(""); (*boost::get_error_info<tag_debug_trace>(e2)) += e.what(); #endif } and then: catch (my_low_level_exception& e) { my_high_level_exception e2; ... fill in e2 add_debug_trace(e2,e); throw e2; }
1. What happens if a "boost::error_info" tag is used multiple times on the same object? Is it an error (exception or assert?), do the instances get combined like a multi-set, or do all instances besides the first (or last) get ignored/dropped? Shouldn't this be documented?
Storing data under a tag overwrites the previous data stored under the same tag. Yes, this behavior should be documented.
2. Shouldn't all inheritance of "boost::exception," especially via "enable_error_info," be of the virtual kind?
This is another thing I didn't pay much attention to in the documentation, because deriving boost::exception is the same as deriving std::exception. As for enable_error_info, I am planning to change it to not inject boost::exception as a base if it already is a base. This would allow enable_error_info to be used in boost::throw_exception regardless of whether a particular exception type derives from boost::exception, or not. This nullifies the concerns whether enable_error_info uses virtual or non-virtual inheritance.
About the compile safety of tags: I prefere to have them statically checked. Acesibility of the tags could be limited only for relevant parts of an application thus removing chance to depend on such internal details in urelated code. String based indentification, if used, should be an option, not mandatory.
I agree with your preference for compile-time type safety. If there is a strong expert opinion that string-based identification is preferable, we could switch to string-based identification but in that case I would drop the tag-based identification altogether.
It would be ideal, if the library is accepted into Boost that all currently thrown exception from Boost libraries (e.g. archive_exception from Serialization) descend from boost::exception. (I am bot holding my breath, this is just an unreachable ideal.)
Boost Exception is most useful when you communicate with 3rd-party or user code that throws exceptions that derive from boost::exception. This would only be possible if boost::exception becomes an universal base class for exception types, much like std::exception is, which is why I've made this library available for consideration as a Boost library. If Boost Exception is accepted to Boost, I wouldn't call making all boost exceptions derive from boost::exception unreachable ideal; in fact enable_error_info is a straight-forward way to reach it. Emil Dotchevski

Please don't consider this a review of the Exception library. I am neither for nor against it. This is more of my gripe session so please forgive me. The exception library is like C++0x, "it is nothing that I asked and wished for but everything I didn't know that I needed". This is both bad, more like mildly disappointing cause of the 10+ year wait, and exceptionally good. My 3 wishes for C++ are standardized dll's, standardized reflection and standardized stack traces. The exception library touches on my 3rd wish/gripe. It is great that a filename can be associated with an exception thrown at a lower level that didn't know that information. Yet, the additional information is purely optional. There is no guarantee that the rethrower ever added the information. As such the final catcher will have to do a lot of tests to see if the information exists. However, it doesn't seem to do anything to get me one step closer of generating a stack trace when the exception is thrown or at least when the boost::exception is constructed. There are no callbacks/hooks to add this capability to everything that derives from boost::exception when the data is not optional. It is not good enough for such stack trace and potential other information to sometimes be there as it is information that should always be available. It is as specialized as my wish and maybe doesn't deserve the name boost::exception. Maybe it should be renamed to boost::map_exception, boost::tuple_exception, boost::additional_optional_information_exception or something similar and in the future an exception class that provides stack traces can be called boost::stacktrace_exception. Let's keep, reserve, the word boost::exception for the day when it handles most everyone basic exception needs; both sets of functionality. P.S. Add method "array<int> getStackTrace()" or "fillStackTrace(array<int>&)" which would provide the trace of the EIPs would go a long way. This could be an empty array when it couldn't be done as in inconsistent register based passing of parameters. It could be simple machine language stack tracing that can't handle inline functions or more complex that relies on internal symbol information, because some information is better than nothing. The EIPs could latter be reconciled using MAP files or .pdbs. The main thing is the pseudo-standard abstraction.

<snip> It is great that a filename can be associated with an exception thrown at a lower level that didn't know that information. Yet, the additional information is purely optional. There is no guarantee that the rethrower ever added the information. As such the final catcher will have to do a lot of tests to see if the information exists.
There is no guarantee in general, meaning that you can't write a universal final catcher that deals with exceptions regardless of what they are, what context they were created in, and what contexts they passed through. However, catchers don't need to be universal. Consider this example of 3 contexts: 1) Mid-level code opens a file, creates a serializer object, and calls a low-level library to read something. 2) The low-level lib throws an exception without a file name because it's using the serializer object, which in principle could be reading from some non-file source. 3) High-level code catches the exception and handles it. In this scenario, 3) doesn't need to deal with exceptions that lack a file name, because the mid-level context should add it (if a file name is missing, we have a bug.) A reasonable behavior is to assert in this case. In principle it is possible that at 3) we catch an exception that legitimately lacks a file name, but that simply means that it did not pass through 1). Still, the solution doesn't need to be universal. At some point, someone needs to "know" about all possible exceptions that can reach a particular high-level exception-handling context, and handle them.
However, it doesn't seem to do anything to get me one step closer of generating a stack trace when the exception is thrown or at least when the boost::exception is constructed.
Stack traces can be stored in boost::exception just as any other error_info. If a particular compiler/platform provides an interface for obtaining a stack trace, I don't see why it shouldn't be captured and stored in the exception by the boost::exception constructor.
There are no callbacks/hooks to add this capability to everything that derives from boost::exception when the data is not optional. It is not good enough for such stack trace and potential other information to sometimes be there as it is information that should always be available.
If some information is not required at the point of the throw, but is required at the point of the catch, you assert() on get_error_info. If some information is required at the point of the throw, then you make your exception class' constructor take that information as argument(s).
It is as specialized as my wish and maybe doesn't deserve the name boost::exception. Maybe it should be renamed to boost::map_exception, boost::tuple_exception, boost::additional_optional_information_exception or something similar and in the future an exception class that provides stack traces can be called boost::stacktrace_exception. Let's keep, reserve, the word boost::exception for the day when it handles most everyone basic exception needs; both sets of functionality.
std::exception does not solve all of your problems either, but it does "deserve" its name because it's purpose is to be the universal base class of all exceptions. boost::exception can be viewed as an extension of std::exception which adds an important piece of missing functionality. If you feel that boost::exception lacks other important functionality to "deserve" its ambitions to be the universal base class of all boost exceptions, the solution is not to rename boost::exception, but to add the missing functionality. Emil Dotchevski

Emil Dotchevski wrote:
2. Cloning of the exception. When I catch exception thrown in a worker thread I create clone of it and throw it in the thread which requested the task to be executed in separate thread. It looks like:
struct my_exc : public boost::exception { virtual my_exc* clone() { return new *this; } };
I'd created common abstract subclass because of it, perhaps it would make sense somehow to support the feature.
I see the http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html now but it doesn't help me here and now, I think.
I believe that cloning exceptions is outside the scope of Boost Exception.
Are you sure? boost::exception can't be a replacement for (modified, according to the paper referenced above) std::exception without at least providing the abstract member functions. Further, putting an exception that supports cloning through 'enable_error_info' currently gives us an exception that only clones a subobject, while it's actually quite inviting to have the most derived (, unspecified) exception class implement clone/rethrow stuff, isn't it?
2. Shouldn't all inheritance of "boost::exception," especially via "enable_error_info," be of the virtual kind?
This is another thing I didn't pay much attention to in the documentation, because deriving boost::exception is the same as deriving std::exception.
As for enable_error_info, I am planning to change it to not inject boost::exception as a base if it already is a base. This would allow enable_error_info to be used in boost::throw_exception regardless of whether a particular exception type derives from boost::exception, or not. This nullifies the concerns whether enable_error_info uses virtual or non-virtual inheritance.
Sounds like a good plan to me. BTW. what do you think about making the other (, inherited) exception accessible through the interface of boost::exception (that is, using a reserved tag and having 'what' call inherited 'what' for formatting)? Regards, Tobias Schwinger - Review Manager -

I see the http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html now but it doesn't help me here and now, I think.
I believe that cloning exceptions is outside the scope of Boost Exception.
Are you sure?
In n2179 exception cloning and re-throwing is decoupled from any particular exception type.
Further, putting an exception that supports cloning through 'enable_error_info' currently gives us an exception that only clones a subobject, while it's actually quite inviting to have the most derived (, unspecified) exception class implement clone/rethrow stuff, isn't it?
enable_error_info can not clone an exception; that is, you can not catch a (base) type, and then throw a copy of it using enable_error_info to inject boost::exception as a base. enable_error_info can only be used before the exception object is passed to the implementation by a throw. At that time the exception object can be copied by the copy constructor.
<snip>
BTW. what do you think about making the other (, inherited) exception accessible through the interface of boost::exception (that is, using a reserved tag and having 'what' call inherited 'what' for formatting)?
To clarify what you mean (correct me if I'm wrong): you throw an exception using enable_error_info, for example: throw boost::enable_error_info(std::bad_alloc()); As per enable_error_info specification, you can catch this exception as std::bad_alloc, or as boost::exception, and you want to be able to get to the bad_alloc sub-object from a boost::exception pointer. I think that there is no need for this functionality as you can simply use dynamic_cast (though the typical use of dynamic_cast in this case would be to get to the boost::exception sub-object after you catch a bad_alloc.) But I agree that it's a good idea to override what() in the type enable_error_info throws. Thanks, Emil Dotchevski

Emil Dotchevski wrote:
I see the http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html now but it doesn't help me here and now, I think. I believe that cloning exceptions is outside the scope of Boost Exception. Are you sure?
In n2179 exception cloning and re-throwing is decoupled from any particular exception type.
Further, putting an exception that supports cloning through 'enable_error_info' currently gives us an exception that only clones a subobject, while it's actually quite inviting to have the most derived (, unspecified) exception class implement clone/rethrow stuff, isn't it?
enable_error_info can not clone an exception; that is, you can not catch a (base) type, and then throw a copy of it using enable_error_info to inject boost::exception as a base.
Not quite what I was talking about. You instantiate 'detail::unspecified_exception_type<...>' (or so), which could very well implement cloning as all types are known at this point.
BTW. what do you think about making the other (, inherited) exception accessible through the interface of boost::exception (that is, using a reserved tag and having 'what' call inherited 'what' for formatting)?
To clarify what you mean (correct me if I'm wrong): you throw an exception using enable_error_info, for example:
throw boost::enable_error_info(std::bad_alloc());
As per enable_error_info specification, you can catch this exception as std::bad_alloc, or as boost::exception, and you want to be able to get to the bad_alloc sub-object from a boost::exception pointer.
I want to catch 'boost::exception' and have 'bad_alloc' as a first class element within it.
I think that there is no need for this functionality as you can simply use dynamic_cast (though the typical use of dynamic_cast in this case would be to get to the boost::exception sub-object after you catch a bad_alloc.)
Yes, but it's very irregular and sorta ugly as we're dealing with two independent (but munged-together) objects that both carry incomplete information.
But I agree that it's a good idea to override what() in the type enable_error_info throws.
No, it seems to be a misunderstanding, again. And frankly it's not a good idea at all, IMO. Personally I'm not even convinced that 'enable_error_info' is a good idea to begin with... Regards, Tobias

"Emil Dotchevski" wrote: "Pavel Vozenilek" wrote:
1. exception/exception.hpp (btw, this file name can easily confuse one with the same filename one level down) - assert() may be replaced with BOOST_ASSERT or even better commented out. People may use their own assert implementation and this would make the exception module (something what should be really the core part) dependent on something else.
If there is a requirement for boost libraries to use BOOST_ASSERT, I'll make that change if the library is officially accepted in Boost.
No, there's no such requirement for BOOST_ASSERT and the macro is not universally used. I asked this because I use my own (feature rich) assert and this implementation intentionally clashes with the default assert and due to the structure of a project the best solution for me was to remove the asserts from boost::exception (under assumption there's no chance of them ever being triggered).
2. shared_ptr<> may be replaced by intrusive_ptr<> or home made equivalent. This (a) reduces dependency on other Boost part (shared_ptr does depend on quite few other libs) and (b) the atomic locking used by shared_ptr would be eliminated. This would help a little bit on multiprocessor systems - lock may be hundredths of cycles and during the time access to the memory bus is disabled.
(a) is a valid concern, OTOH shared_ptr is such a low level component of Boost that -- as careful as I am in avoiding physical coupling -- I don't consider it a real dependency; rather, it's a tool for avoiding dependencies.
(b) can be addressed if someone reports having performance issues with Boost Exception. This is highly unlikely since we're comparing the time it takes to copy a single shared_ptr once (at the time of the initial throw) vs. the time it takes for the implementation to unroll the stack until a suitable catch is found.
Ad (a) and Peter Dimov's reply: few years ago when I bcopy'ed shared_ptr it depended on a lot of other code. This may be mistaken or obsoleted. Ad (b): my personal opinion is that for such general purpose library people would feel safer if it is reasonably optimized even for slow paths. Other (over-)optimizations that may be considered: * some to-string conversions could be handled w/o stringstream * if it is applicable (I didn't measured) code bloat caused by inlining on slow paths may be reduced for MSVC compiled by using __declspec(noinline). Strange combination __declspec(noinline) inline void foo() {} is also possible with desirable effects (http://blogs.msdn.com/freik/archive/2005/10/26/485276.aspx)
1. As mentioned above, the ability to iterate through values and/or pass a visitor.
Iteration presumes that you catch a boost::exception knowing nothing about the semantics of the actual exception type that was thrown, yet you want to make use of the data it contains.
One use case would be to log all data contained in an (unknown) exception, for debugging purposes; this is supported by boost::exception::what().
Another use case is when you want to catch exception type A and throw exception type B instead, such that the B object contains all the data from the A object plus some additional information. However, one of the motivations for Boost Exception is that it helps avoid the need to translate exception types, since you can catch A as boost::exception &, add whatever relevant data you have, and then re-throw the original A object.
Use case for iterator over values: I may want an automated checker that verifies that exception A contains only somewhere specified values and nothing else.
4. I would like the ability to collect traces generated by what() function in DEBUG mode, something as:
catch (my_low_level_exception& e) { my_high_level_exception e2; ... fill in e2 e2.add_debug_trace(e); // adds e.what() somewhere, no-op in release mode throw e2; }
Would this work for you:
struct tag_debug_trace: boost::error_info<std::string> { };
void add_debug_trace( boost::exception & e2, boost::exception & e ) { #ifdef _DEBUG if( !boost::get_error_info<tag_debug_trace>(e2) ) e2 << boost::error_info<tag_debug_trace>(""); (*boost::get_error_info<tag_debug_trace>(e2)) += e.what(); #endif }
and then:
catch (my_low_level_exception& e) { my_high_level_exception e2; ... fill in e2 add_debug_trace(e2,e); throw e2; }
Yeah, something like that with visual separation between different exception objects and as a standard part of the library. --------------------------------- About an other poster wish for standardized stack trace: I wrote at least two stack trace libraries requiring manually added macro like void foo() { STACK_TRACE; .... } It would be handy to get something like that semi-standardized next to the boost::exception and able to cooperate automatically with this library. My implementation got quite complicated: thread safe, able to provide one stack trace for catch (exc1&) { throw exc2; } to store stack traces for sucessfully processed exceptions, it was able to collect ad-hoc information like boost::exception can, etc. /Pavel

<snip> Other (over-)optimizations that may be considered:
* some to-string conversions could be handled w/o stringstream
This can be done without altering the proposed interface. Boost Exception uses an unqualified call to to_string to convert an error_info value to string (for automatic inclusion in the what() message.) The user also has the option of disabling the to_string conversion at the point when a tag type is registered. Currently, Boost Exception contains a generic to_string overload which attempts to use an ostringstream object to do the conversion. However, this is just an implementation detail.
* if it is applicable (I didn't measured) code bloat caused by inlining on slow paths may be reduced for MSVC compiled by using __declspec(noinline).
Yes, other platform-specific optimizations and additions (such as automatic stack trace) are also possible within the proposed framework.
1. As mentioned above, the ability to iterate through values and/or pass a visitor. <snip> Use case for iterator over values: I may want an automated checker that verifies that exception A contains only somewhere specified values and nothing else.
The "and nothing else" part can not be implemented on top of the proposed framework. OTOH, it is not very practical because it is very common for boost exception objects to contain additional information unknown to a particular context. For example, there might be platform-specific debugging information which shouldn't render an exception object invalid. If you don't insist on "and nothing else", what you want can be implemented without adding iteration to Boost Exception.
4. I would like the ability to collect traces generated by what() function in DEBUG mode, something as: <snip>
Would this work for you:
struct tag_debug_trace: boost::error_info<std::string> { };
void add_debug_trace( boost::exception & e2, boost::exception & e ) { #ifdef _DEBUG if( !boost::get_error_info<tag_debug_trace>(e2) ) e2 << boost::error_info<tag_debug_trace>(""); (*boost::get_error_info<tag_debug_trace>(e2)) += e.what(); #endif }
and then:
catch (my_low_level_exception& e) { my_high_level_exception e2; ... fill in e2 add_debug_trace(e2,e); throw e2; }
Yeah, something like that with visual separation between different exception objects and as a standard part of the library.
I mean, this type of functionality is often application-specific and is best left out of the Boost Exception specification, in my opinion. This was just an illustration that what you want can already be done using Boost Exception. Emil Dotchevski

"Emil Dotchevski" wrote: "Pavel Vozenilek" wrote:
1. As mentioned above, the ability to iterate through values and/or pass a visitor. <snip> Use case for iterator over values: I may want an automated checker that verifies that exception A contains only somewhere specified values and nothing else.
The "and nothing else" part can not be implemented on top of the proposed framework. OTOH, it is not very practical because it is very common for boost exception objects to contain additional information unknown to a particular context. For example, there might be platform-specific debugging information which shouldn't render an exception object invalid.
If you don't insist on "and nothing else", what you want can be implemented without adding iteration to Boost Exception.
Let say we disagree here. I see iteration (or less conveniently visitation) as a useful mechanism for not-yet-designed tools that would automatically process, convert or check validity of exceptions
4. I would like the ability to collect traces generated by what() function in DEBUG mode, something as: <snip>
Would this work for you:
struct tag_debug_trace: boost::error_info<std::string> { };
void add_debug_trace( boost::exception & e2, boost::exception & e ) { #ifdef _DEBUG if( !boost::get_error_info<tag_debug_trace>(e2) ) e2 << boost::error_info<tag_debug_trace>(""); (*boost::get_error_info<tag_debug_trace>(e2)) += e.what(); #endif }
and then:
catch (my_low_level_exception& e) { my_high_level_exception e2; ... fill in e2 add_debug_trace(e2,e); throw e2; }
Yeah, something like that with visual separation between different exception objects and as a standard part of the library.
I mean, this type of functionality is often application-specific and is best left out of the Boost Exception specification, in my opinion. This was just an illustration that what you want can already be done using Boost Exception.
Three points: 1. If it is in Boost there's no need to create it, it would be documented and always available 2. If it gets semi-standardized in Boost it would be much more likely used than a home grown equivalent. If everyone creates their own mechanism cooperation would not be smooth. 3. It doesn't prevent application specific solution. /Pavel

1. As mentioned above, the ability to iterate through values and/or pass a visitor. <snip> Use case for iterator over values: I may want an automated checker that verifies that exception A contains only somewhere specified values and nothing else. If you don't insist on "and nothing else", what you want can be implemented without adding iteration to Boost Exception.
Let say we disagree here. I see iteration (or less conveniently visitation) as a useful mechanism for not-yet-designed tools that would automatically process, convert or check validity of exceptions.
Perhaps I don't understand exactly what you mean by iteration. I see something like: struct error_info_iterator_value_type { std::type_info const * tag_type; std::type_info const * value_type; void const * value; std::string value_as_string; }; I might be blind, but I can't see how could the iteration give me anything more useful, and I can't think of any useful generic processing that can be done with this (you can dump all this in a string, but that's exactly what boost::exception::what() does.) Emil Dotchevski

It would be useful for this purpose to provide an ability to iterate through the values or ability to pass a visitor or both. IOW, very long and hard to read lines. I would welcome some way to fill in the exception in more compact form. I, however, do not have any suggestion how to do it. 4. I would like the ability to collect traces generated by what() function
I've been playing around with Exception tonight and have a variation with the following changes: 1. A more concise throw statement is desirable. In the variation, both tagged and untagged fields can be added. The order of the fields is preserved, using a linked list, instead of a map. Formatting of the "what string" is still delayed until exception::what is called. The bare minimum the amount of code needed to throw an exception with a message that contains both a useful message for debugging and marked fields for reporting back to the user. throw exception() << "The frobknob is out of range: its value is " << error_info<value_tag>(value)<< " but the maximum value is " << max; 2. The potential for a null return value from get_error_info is going to cause bugs. In this variation, this potential is eliminated by using a visitor. A visitor can be applied to the fields to manipulate individual values. The visitor is applied to each field with a matching type. The field need not be tagged in order to be visited. catch(exception&e) { using lambda; int x(0); e.visit(on_error_info(value_tag(),var(x)=_1)); cerr << "frobknob value is " << x << "\n"; int y(0); e.visit(on_error_info<int>(var(y)=_1)); cerr << "max was " << y << "\n"; } 3. Sometimes you really want to throw a different exception type that better describes the context of the error. In this case, the list of fields from one exception can be added to a second exception. This affects both exception::what and exception::visit. catch(exception&e) { throw can_not_load_file() << "could not open " << error_info<filename_tag>(filename) << " because " << e; } Source code is here http://joshuanapoli.com/exception -Joshua

On 10/19/07, Josh Napoli <jnapoli@actuality-systems.com> wrote:
<snip> throw exception() << "The frobknob is out of range: its value is " << error_info<value_tag>(value)<< " but the maximum value is " << max;
Why not throw a std::ostringstream directly then? :)
participants (7)
-
Emil Dotchevski
-
Igor.Smirnov@cern.ch
-
Jarrad Waterloo
-
Josh Napoli
-
Pavel Vozenilek
-
Peter Dimov
-
Tobias Schwinger