Re: [boost] Exception lib?

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Emil Dotchevski Sent: Tuesday, June 20, 2006 2:43 PM To: boost@lists.boost.org Subject: [boost] Exception lib?
I came up with a solution which seems to solve the problem, and I wanted to see if there is sufficient interest in it to be added to boost.
I was reading this and thinking, is this necessary? But it is. Quite an interesting approach that I've never thought of. How does the catch handler actually process this information? Could you write an example that would show how you could give detailed information? I think I can see it, but I'm not quite sure. Thanks Sohail

I was reading this and thinking, is this necessary? But it is. Quite an interesting approach that I've never thought of.
How does the catch handler actually process this information?
Could you write an example that would show how you could give detailed information? I think I can see it, but I'm not quite sure.
The main issue for me was to decouple the context information from the type of the exception, and the main reason for this is that the same exception could be thrown in different contexts (by the same throw statement), and require different information attached to it. So the question then was how to store the attachments. As I mentioned in my previous post, in my implementation I wrote a class called info, which stores the attachments in a map of boost::any objects, associated by their type_info. Then I wrote a simple "info wrapper" class template, something along the following lines: template <class T,class Tag> class info_wrapper //value type { public: info_wrapper( T const & v ): value_(v) { } T const & get() const { return value_; } private: T value_; }; template <class Tag,class T> info_wrapper<T,Tag> wrap_info( T const & v ) { return info_wrapper<T,Tag>(v); } template <class Tag,class T> T const * get_info( info const & xi ) { if( info_wrapper<T,Tag> const * w=xi->get< info_wrapper<T,Tag> >() ) return &w->get(); else return 0; } The xi->get<> member function template returns a pointer to the attachment of the specified type stored in the info object, or null if the info object doesn't store an attachment of the specified type. Then I have two other function templates: template <class Tag> info_wrapper<std::string,Tag> wrap_string( char const * s ) { return wrap_info<Tag>( std::string(s) ); } template <class Tag> char const * get_string( info const & xi ) { if( std::string const * s = get_info<Tag,std::string>(xi) ) return s.c_str(); else return 0; } Now, with this setup I can attach strings to exceptions, and access them by their tags, like so: ... class tag_file_name; //incomplete info & xi = ....; xi.add( wrap_string<tag_file_name>("filename.txt") ); At the catch site, I can extract the file name like this: ... catch( read_error & x ) { if( info * xi = dynamic_cast<info *>(&x) ) if( char const * file_name = get_string<tag_file_name>(*xi) ) { //file_name is the file name attached to the read_error exception. } } Here is an example which demonstrates the necessity of this system. Consider the situation when you copy one file to another, by repeatedly calling a "read" function and a "write" function, each of which could throw. Clearly, in the context of this copy operation, an exception should have both the source and the target file name (in a more typical context you only need one file name attached.) This is how you can accomplish this with the presented system: class tag_src_file_name; class tag_dst_file_name; ... boost::shared_ptr<FILE> src = io::fopen(src_name,"rb"); boost::shared_ptr<FILE> dst = io::fopen(dst_name,"wb"); try { while( !done ) { read(src,buffer); //throws by throw failed<read_error>() write(dst,buffer); //throws by throw failed<write_error>() } } catch( info & xi ) { xi.add( wrap_string<tag_src_file_name>(src_name) ); xi.add( wrap_string<tag_dst_file_name>(dst_name) ); throw; } Assuming both read_error and write_error derive from io_error, here's what the catch could look like: catch( io_error & x ) { if( info * xi = dynamic_cast<info *>(&x) ) { if( char const * file_name = get_string<tag_file_name>(*xi) ) ....; //we've got a file name if( char const * src_name = get_string<tag_src_file_name>(*xi) ) ....; //we've got a source file name if( char const * dst_name = get_string<tag_dst_file_name>(*xi) ) ....; //we've got a destination file name } } Of course, read_error and write_error exceptions could be processed separately, if necessary. And finally, I indicated in my previous post that attachments can be added directly in the throw-statement. Please don't flame me for the operator<< overload, it is necessary to allow composition of attachments. The simplest form of throwing would be this: throw failed<read_error>(); The above throw-expression throws an exception with no attachments (but with the ability to have stuff attached to it later.) You can also do this: throw failed<read_error>() << wrap_errno(); Here, wrap_errno is a function that captures the current errno value, and returns it as an object of some type, which is then stored in the info sub-object of the exception being thrown. Of course, you can attach more stuff directly in the throw-expression, by following with more calls to operator<<. Here is another example from my own code: namespace file { size_t fread( void * buffer, size_t size, size_t count, boost::shared_ptr<FILE> const & f ) { assert( f ); size_t nr = ::fread(buffer,size,count,f.get()); if( !ferror(f.get()) ) return nr; else throw failed<fread_error>() << wrap_errno() << wrap_function("fread") << boost::weak_ptr<FILE>(f); } } HTH, Emil

Emil Dotchevski wrote:
catch( read_error & x ) { if( info * xi = dynamic_cast<info *>(&x) )
you do not need dynamic_cast here, or you could just put catch(info& x) above read_error?
catch( info & xi ) { xi.add( wrap_string<tag_src_file_name>(src_name) ); xi.add( wrap_string<tag_dst_file_name>(dst_name) ); throw; }
wow! I love it! I could add context information at higher levels of application (when exception is "bubbling up") so that user gets relevant and high-level information that he can match to what he was just doing. Nice!
catch( io_error & x ) { if( info * xi = dynamic_cast<info *>(&x) )
again, why dynamic_cast?
And finally, I indicated in my previous post that attachments can be added directly in the throw-statement.
maybe these attachments could be separate and more general utility? I played with idea of employing similar functionality in implementation of the context design pattern.
throw failed<fread_error>() << wrap_errno() << wrap_function("fread") << boost::weak_ptr<FILE>(f);
although I'm one of these malcontents who wince seeing << or >> applied anywhere where they look cool, here they not only look cool but also make a lot of sense. Nice again! B.

Emil Dotchevski wrote:
catch( read_error & x ) { if( info * xi = dynamic_cast<info *>(&x) )
you do not need dynamic_cast here, or you could just put catch(info& x) above read_error?
If your program is somewhat simple, you may be tempted to catch(info&), and then show the user everything you could dig out of the info object. But do you really not care what went wrong? Typically you do. For example, if you catch(render_error&) you wouldn't be probing the info sub-object for file names, would you? I think how you catch your exceptions should remain unchanged even if you begin using the presented exception lib. The exceptions you throw using the 'failed' function template are still part of the exception class hierarchy you have in your program. The only thing that changes is that you can clean your exception class hierarchy from all types that don't indicate a class of errors, but instead serve as holders of additional information. The dynamic_cast also makes it easier to roll the exception lib into existing code. You can start converting your throws from throw my_error() to throw failed<my_error>(). Meanwhile, you still catch(my_error&), so that regardless of how you throw, you will handle the exception correctly. --Emil
participants (3)
-
Bronek Kozicki
-
Emil Dotchevski
-
Sohail Somani