<p>All exception types that derive from boost::<spanclass="RenoLink"><ahref="exception.html">exception</a></span> can be used as type-safe containers of arbitrary data objects, while complying with the no-throw requirements (15.5.1) of the ANSI C++ standard for exception types. Data can be added to a boost::<spanclass="RenoLink"><ahref="exception.html">exception</a></span> at the time of the throw, or at a later time.</p>
<p>First, we instantiate the <spanclass="RenoLink"><ahref="error_info.html">error_info</a></span> template using a unique identifier -- tag_errno, and the type of the info it identifies -- int. This provides compile-time type safety for the various values stored in exception objects.</p>
<p>Second, we define class my_error, which derives from boost::<spanclass="RenoLink"><ahref="exception.html">exception</a></span>.</p>
<p>Finally, (3) illustrates how the typedef from (1) can be used with <spanclass="RenoLink"><ahref="exception_operator_shl.html">operator<<</a></span> to store values in exception objects at the point of the throw.</p>
<p>The <spanclass="RenoLink"><ahref="get_error_info.html">get_error_info</a></span> function template is instantiated with the typedef from (1), and is passed an exception object of a polymorphic type. If the exception object contains the requested value, the returned <spanclass="RenoLink"><ahref="http://www.boost.org/libs/smart_ptr/shared_ptr.htm">shared_ptr</a></span> will point to it; otherwise an empty <spanclass="RenoLink"><ahref="http://www.boost.org/libs/smart_ptr/shared_ptr.htm">shared_ptr</a></span> is returned.</p>
<p>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:</p>
<pre>#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("????");
}</pre>
<p>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.</p>
<p>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.)</p>
<p>A better solution is to make class file_read_error derive (possibly indirectly) from boost::<spanclass="RenoLink"><ahref="exception.html">exception</a></span>, and free the file_read function from the burden of storing the file name in exceptions it throws:</p>
<p>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:</p>
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::<spanclass="RenoLink"><ahref="exception.html">exception</a></span>& e )
{
e << file_name_info(file_name);
throw;
}
}</pre>
<p>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::<spanclass="RenoLink"><ahref="exception.html">exception</a></span> object, stores the file name, and re-throws using a throw-expression with no operand (15.1.6). The rationale for catching any boost::<spanclass="RenoLink"><ahref="exception.html">exception</a></span> object is that the file name is relevant to any failure that occurs in parse_file, <i>even if the failure is unrelated to file I/O</i>.</p>
<p>As usual, the stored data can be retrieved using <spanclass="RenoLink"><ahref="get_error_info.html">get_error_info</a></span>.</p>
<p>The code snippet below demonstrates how boost::<spanclass="RenoLink"><ahref="http://www.boost.org/libs/tuple/doc/tuple_users_guide.html">tuple</a></span> can be used to bundle the name of the function that failed, together with the reported errno so that they can be added to exception objects more conveniently together:</p>
class file_open_error: public boost::<spanclass="RenoLink"><ahref="exception.html">exception</a></span> { };
boost::shared_ptr<FILE>
file_open( char const * name, char const * mode )
{
if( FILE * f=fopen(name,mode) )
return boost::shared_ptr<FILE>(f,fclose);
else
throw file_open_error() <<
file_name_info(name) <<
clib_failure("fopen",errno);
}</pre>
<p>Note that the members of a boost::<spanclass="RenoLink"><ahref="http://www.boost.org/libs/tuple/doc/tuple_users_guide.html">tuple</a></span> are stored separately in exception objects; they can only be retrieved individually, using <spanclass="RenoLink"><ahref="get_error_info.html">get_error_info</a></span>.</p>