
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