[exception] ostringstream like

Hi, I would like to propose an "error_info_sstream" class, allowing to do: Point3D p; int id; boost::filesystem::path path; ... BOOST_THROW_EXCEPTION( exception::Logic() << exception::user( "Foo failed." ) << exception::dev() + "Foo failed at index " + id + " at position " + p + "." << exception::filename( path->filename() ) ); With a declaration, like that: namespace MyProject { namespace exception { typedef ::boost::error_info<struct tag_userMessage,::boost::error_info_sstream> user; typedef ::boost::error_info<struct tag_devMessage,::boost::error_info_sstream> dev; typedef ::boost::errinfo_file_name filename; struct Logic : virtual public ::std::exception, virtual public ::boost::exception {}; } } The idea is to remove such things: BOOST_THROW_EXCEPTION( exception::Logic() << exception::user( "Foo failed." ) << exception::dev( std::string("Foo failed at index ") + boost::lexical_cast<std::string>(id) + " at position [x:" + boost::lexical_cast<std::string>(p.x) + ",y:" + boost::lexical_cast<std::string>(p.y) + ",z:" + boost::lexical_cast<std::string>(p.z) + "]." ) << exception::filename( path->filename() ) ); or std::ostringstream msg; msg << "Foo failed at index " << id << " at position " << p << "."; BOOST_THROW_EXCEPTION( exception::Logic() << exception::user( "Foo failed." ) << exception::dev(msg) << exception::filename( path->filename() ) ); The proposed solution is to have an error_info with an operator+, doing the same thing as an operator<< on an ostringstream. 1) Are you agree with this idea ? 2) Are you agree with this implementation ? I write this without understanding all internal things inside boost::exception. 3) Are you interested to add something like that in boost::exception ? Best regards, Fabien Castan namespace boost { struct error_info_sstream { typedef std::ostringstream value_type; value_type _v; }; inline std::string to_string( const error_info_sstream& x ) { return x._v.str(); } template<class Tag> class error_info<Tag, error_info_sstream>: public exception_detail::error_info_base { public: typedef error_info_sstream T; typedef error_info<Tag,T> This; typedef T value_type; error_info(){} error_info( const This& v ) { _value._v << v._value._v.str(); } template<typename V> error_info( const V& value ) { _value._v << value; } ~error_info() throw() {} template<typename V> This& operator+( const V& v ) { _value._v << v; return *this; } const value_type& value() const { return _value; } value_type& value() { return _value; } private: std::string tag_typeid_name() const { return tag_type_name<Tag>(); } std::string value_as_string() const { return _value._v.str(); } value_type _value; }; }

On 15/08/10 14:04, fabien wrote:
I would like to propose an "error_info_sstream" class Well, exception are just here to gather and carry diagnostic info, not full fledged error message as it limits applciability (like localization) and may fail to allocate the needed memory for the string and stream, thus maskign the initial exception with some bad_alloc.

On Sun, Aug 15, 2010 at 5:22 AM, joel falcou <joel.falcou@lri.fr> wrote:
On 15/08/10 14:04, fabien wrote:
I would like to propose an "error_info_sstream" class
Well, exception are just here to gather and carry diagnostic info, not full fledged error message as it limits applciability (like localization)
This is correct, formatting an error message is always beyond the scope of the code that throws the exception.
and may fail to allocate the needed memory for the string and stream, thus maskign the initial exception with some bad_alloc.
This concern is not valid because the C++ standard permits the runtime code to run out of memory while trying to throw an exception, which presumably may result in a std::bad_alloc. Also, in Boost Exception adding error_info has the postcondition that it has been added, meaning you may get another exception when you try to add error_info. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

At Sun, 15 Aug 2010 11:26:30 -0700, Emil Dotchevski wrote:
and may fail to allocate the needed memory for the string and stream, thus maskign the initial exception with some bad_alloc.
This concern is not valid because the C++ standard permits the runtime code
Do you mean the runtime library?
to run out of memory while trying to throw an exception, which presumably may result in a std::bad_alloc.
If not, I don't think that invalidates the concern at all. It's application- dependent whether or not the originally-intended exception is more important information than the knowledge that memory was running short at the point of the throw. If so, I wouldn't presume that it would throw bad_alloc in that case, in real implementations. Usually this sort of thing falls under "violating implementation limits" and invokes undefined behavior. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Sun, Aug 15, 2010 at 11:56 AM, David Abrahams <dave@boostpro.com> wrote:
At Sun, 15 Aug 2010 11:26:30 -0700, Emil Dotchevski wrote:
and may fail to allocate the needed memory for the string and stream, thus maskign the initial exception with some bad_alloc.
This concern is not valid because the C++ standard permits the runtime code
Do you mean the runtime library?
No, I mean the generated code.
to run out of memory while trying to throw an exception, which presumably may result in a std::bad_alloc.
If not, I don't think that invalidates the concern at all. It's application- dependent whether or not the originally-intended exception is more important information than the knowledge that memory was running short at the point of the throw.
If so, I wouldn't presume that it would throw bad_alloc in that case, in real implementations. Usually this sort of thing falls under "violating implementation limits" and invokes undefined behavior.
Stroustrup, The C++ Programming Language, Special Edition, page 371: "Throwing an exception requires an object to throw. A C++ implementation is required to have enough spare memory to be able to throw bad_alloc in case of memory exhaustion. However, it is possible that throwing some other exception will cause memory exhaustion." This stops short of saying that it might result in a std::bad_alloc but it seems permitted. Since throwing (anything other than std::bad_alloc) can fail, you can think of the postcondition of throwing an exception to be that the exception was thrown successfully. Exceptions being the usual way to enforce postconditions, I think it is natural to permit a different exception to propagate instead. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

At Sun, 15 Aug 2010 12:25:50 -0700, Emil Dotchevski wrote:
On Sun, Aug 15, 2010 at 11:56 AM, David Abrahams <dave@boostpro.com> wrote:
At Sun, 15 Aug 2010 11:26:30 -0700, Emil Dotchevski wrote:
and may fail to allocate the needed memory for the string and stream, thus maskign the initial exception with some bad_alloc.
This concern is not valid because the C++ standard permits the runtime code
Do you mean the runtime library?
No, I mean the generated code.
Code generated by what? The throw-expression? Sure. The code that is generated to throw the exception once it has been constructed? I don't think so.
to run out of memory while trying to throw an exception, which presumably may result in a std::bad_alloc.
If not, I don't think that invalidates the concern at all. It's application- dependent whether or not the originally-intended exception is more important information than the knowledge that memory was running short at the point of the throw.
If so, I wouldn't presume that it would throw bad_alloc in that case, in real implementations. Usually this sort of thing falls under "violating implementation limits" and invokes undefined behavior.
Stroustrup, The C++ Programming Language, Special Edition, page 371:
Please, don't quote that section to me. I know where most of that section came from, and it's a rather loose interpretation of the source material (and the standard).
"Throwing an exception requires an object to throw. A C++ implementation is required to have enough spare memory to be able to throw bad_alloc in case of memory exhaustion. However, it is possible that throwing some other exception will cause memory exhaustion."
This stops short of saying that it might result in a std::bad_alloc but it seems permitted.
If you can find a place in the standard that says "throw x" is permitted to throw a bad_alloc when x is not an instance of bad_alloc, I'll buy it. Otherwise, no.
Since throwing (anything other than std::bad_alloc) can fail, you can think of the postcondition of throwing an exception to be that the exception was thrown successfully. Exceptions being the usual way to enforce postconditions, I think it is natural to permit a different exception to propagate instead.
It may be natural. That still doesn't invalidate the OP's concern. Mostly I'm reacting to your statement that the concern isn't valid, which seems far too strong. If I keep careful control over my exception types---for example, if I only throw PODs---I can reasonably expect that "throw x;" throws x until I violate implementation limits (and then I suppose then a bad_alloc is as good an expression of undefined behavior as anything else). -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Sun, Aug 15, 2010 at 12:45 PM, David Abrahams <dave@boostpro.com> wrote:
At Sun, 15 Aug 2010 12:25:50 -0700, Emil Dotchevski wrote:
Stroustrup, The C++ Programming Language, Special Edition, page 371:
Please, don't quote that section to me. I know where most of that section came from, and it's a rather loose interpretation of the source material (and the standard).
Evidently you're more familiar than me with this matter. The paragraph in the standard that defines how the memory for the exception is allocated has a reference to the section which defines the semantics of the general allocation functions, but this may be a rather loose interpretation of the standard, I'm not an expert.
Since throwing (anything other than std::bad_alloc) can fail, you can think of the postcondition of throwing an exception to be that the exception was thrown successfully. Exceptions being the usual way to enforce postconditions, I think it is natural to permit a different exception to propagate instead.
It may be natural. That still doesn't invalidate the OP's concern. Mostly I'm reacting to your statement that the concern isn't valid, which seems far too strong.
Yes it was far too strong, my bad.
If I keep careful control over my exception types---for example, if I only throw PODs---I can reasonably expect that "throw x;" throws x
You can reasonably expect that even for non-PODs. By the time you end up getting a std::bad_alloc, I don't think it matters much whether it is emitted from some operator new or from a throw (possibly indirectly, from an exception object which allocates memory in its consturctor) -- your process is in a pretty bad state already. The reason why I'm reacting to this type of concerns is that typically they're motivated by a desire to control exactly what exceptions a particular function is permitted to emit. It is this type of concerns that have lead to the infamous exception specifications in the standard. In other cases, these concerns lead to exception type translation code which usually can be summarized as "catch(x) { throw y(x); }. Often, this doesn't make it easier to deal with x, but now the code that deals with x is coupled with y too. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

At Sun, 15 Aug 2010 16:08:47 -0700, Emil Dotchevski wrote:
If I keep careful control over my exception types---for example, if I only throw PODs---I can reasonably expect that "throw x;" throws x
You can reasonably expect that even for non-PODs. By the time you end up getting a std::bad_alloc, I don't think it matters much whether it is emitted from some operator new or from a throw (possibly indirectly, from an exception object which allocates memory in its consturctor)
My point is that whether that matters to the application is not for you---the library author---to decide.
-- your process is in a pretty bad state already.
I wouldn't say that. Out-of-memory is a condition from which recovery is perfectly manageable.
The reason why I'm reacting to this type of concerns is that typically they're motivated by a desire to control exactly what exceptions a particular function is permitted to emit. It is this type of concerns that have lead to the infamous exception specifications in the standard.
Agreed.
In other cases, these concerns lead to exception type translation code which usually can be summarized as "catch(x) { throw y(x); }. Often, this doesn't make it easier to deal with x, but now the code that deals with x is coupled with y too.
Yup. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Sun, Aug 15, 2010 at 5:59 PM, David Abrahams <dave@boostpro.com> wrote:
At Sun, 15 Aug 2010 16:08:47 -0700, Emil Dotchevski wrote:
If I keep careful control over my exception types---for example, if I only throw PODs---I can reasonably expect that "throw x;" throws x
You can reasonably expect that even for non-PODs. By the time you end up getting a std::bad_alloc, I don't think it matters much whether it is emitted from some operator new or from a throw (possibly indirectly, from an exception object which allocates memory in its consturctor)
My point is that whether that matters to the application is not for you---the library author---to decide.
Well, but I have made that decision, at least as far as Boost Exception's error_info is concerned. The postcondition of the op<< defined by the library is that the error_info has been stored in the exception object. Op<< may throw std::bad_alloc or any exception emitted by the error_info's copy constructor, and it only has basic exception safety guarantee. This way, at the catch site we can reason about what error_info objects the exception has: catch(my_error & e) { assert(boost::get_error_info<my_info>(e)!=0); //proceed to format a user-friendly message } which in a top-level context would likely be followed by catch(...) { //oh crap print(boost::current_exception_diagnostic_information()); }
-- your process is in a pretty bad state already.
I wouldn't say that. Out-of-memory is a condition from which recovery is perfectly manageable.
In theory it is, but on most platforms wouldn't you just crawl to a halt due to swapping and/or get killed by the OS or the user? Even if that doesn't happen, how can you reasonably test that your program works correctly in this situation? Certainly, I wouldn't bet my ears that my own programs deal with all possible std::bad_allocs gracefully. :) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

David Abrahams wrote:
At Sun, 15 Aug 2010 16:08:47 -0700, Emil Dotchevski wrote:
If I keep careful control over my exception types---for example, if I only throw PODs---I can reasonably expect that "throw x;" throws x You can reasonably expect that even for non-PODs. By the time you end up getting a std::bad_alloc, I don't think it matters much whether it is emitted from some operator new or from a throw (possibly indirectly, from an exception object which allocates memory in its consturctor)
My point is that whether that matters to the application is not for you---the library author---to decide.
<snip> Sorry to be a little late to chime in, but this issue has been brought up before. Here's a thread to a discussion that Emil and my had back in May 2009 http://thread.gmane.org/gmane.comp.lib.boost.user/47930 Andy.
participants (5)
-
Andy Venikov
-
David Abrahams
-
Emil Dotchevski
-
fabien
-
joel falcou