Sometimes the throw site does not have all the information that is needed at the catch site to make sense of what went wrong. Here is an example:
#include <stdio.h> #include <string> class file_read_error { public: explicit file_read_error( std::string const & fn ): fn_(fn) { }; std::string const & file_name() const { return fn_; } private: std::string fn_; }; void file_read( FILE * f, void * buffer, size_t size ) { if( size!=fread(buffer,1,size,f) ) throw file_read_error("????"); }
We have defined an exception class file_read_error which can store a file name, so that when we catch a file_read_error object, we know which file the failure is related to. However, the file_read function does not have the file name at the time of the throw; all it has is a FILE handle.
One possible solution is to not use FILE handles directly. We could have our own class file which stores both a FILE handle and a file name, and pass that to file_read(). However, this could be problematic if we communicate with 3rd party code that does not use our class file (probably because they have their own similar class.)
A better solution is to make class file_read_error derive (possibly indirectly) from boost::exception, and free the file_read() function from the burden of storing the file name in exceptions it throws:
#include <boost/exception.hpp> #include <stdio.h> #include <errno.h> typedef boost::error_info<struct tag_errno,int> errno_info; class file_read_error: public boost::exception { }; void file_read( FILE * f, void * buffer, size_t size ) { if( size!=fread(buffer,1,size,f) ) throw file_read_error() << errno_info(errno); }
If file_read() detects a failure, it throws an exception which contains the information that is available at the time, namely the errno. Other relevant information, such as the file name, can be added in a context higher up the call stack, where it is known naturally:
#include <boost/exception.hpp> #include <boost/shared_ptr.hpp> #include <stdio.h> #include <string> typedef boost::error_info<struct tag_file_name,std::string> file_name_info; boost::shared_ptr<FILE> file_open( char const * file_name, char const * mode ); void file_read( FILE * f, void * buffer, size_t size ); void parse_file( char const * file_name ) { boost::shared_ptr<FILE> f = file_open(file_name,"rb"); assert(f); try { char buf[1024]; file_read( f.get(), buf, sizeof(buf) ); } catch( boost::exception & e ) { e << file_name_info(file_name); throw; } }
The above function is (almost) exception-neutral -- if an exception is emitted by any function call within the try block, parse_file() does not need to do any real work, but it intercepts any boost::exception object, stores the file name, and re-throws using a throw-expression with no operand (15.1.6). The rationale for catching any boost::exception object is that the file name is relevant to any failure that occurs in parse_file(), even if the failure is unrelated to file I/O.
As usual, the stored data can be retrieved using get_error_info().