Boost Exception的目的是简化异常类层次结构的设计,并帮助编写异常处理和错误报告代码。
Boost Exception提供了一种新的异常类型boost::Exception,允许您在抛出异常后向异常添加数据。所需头文件:boost/exception/all.hpp
动机
“传统”方法的主要问题是,抛出点的可用数据通常不足以让捕获点处理故障。
void
read_file( FILE * f )
{
....
size_t nr=fread(buf,1,count,f);
if( ferror(f) )
throw file_read_error(???);
....
}
....
catch( file_read_error & e )
{
std::cerr << e.file_name();
}
显然,问题是处理程序需要一个文件名,但read_file函数没有把文件名来放入异常对象中;read_file函数只有一个文件指针!
解决方案
只需从boost::exception派生您的异常类型。
自信地限制抛出点只提供自然可用的数据。
在抛出和捕获之间使用与异常无关的上下文,以便在异常冒泡时使用更有用的数据来扩充异常。
例如,在下面的throw语句中,我们只添加errno代码,因为这是此上下文中唯一可用的与故障相关的信息:
struct exception_base: virtual std::exception, virtual boost::exception { };
struct io_error: virtual exception_base { };
struct file_read_error: virtual io_error { };
typedef boost::error_info<struct tag_errno_code,int> errno_code;
void
read_file( FILE * f )
{
....
size_t nr=fread(buf,1,count,f);
if( ferror(f) )
throw file_read_error() << errno_code(errno);
....
}
在更高的异常无关上下文中,我们将文件名添加到异常中
typedef boost::error_info<struct tag_file_name,std::string> file_name;
....
try
{
if( FILE * fp=fopen("foo.txt","rt") )
{
shared_ptr<FILE> f(fp,fclose);
....
read_file(fp); //throws types deriving from boost::exception
do_something();
....
}
else
throw file_open_error() << errno_code(errno);
}
catch( boost::exception & e )
{
e << file_name("foo.txt");
throw;
}
最后,处理程序如何从派生自boost::exception的异常中检索数据:
catch( io_error & e )
{
std::cerr << "I/O Error!\n";
if( std::string const * fn=get_error_info<file_name>(e) )
std::cerr << "File name: " << *fn << "\n";
if( int const * c=get_error_info<errno_code>(e) )
std::cerr << "OS says: " << strerror(*c) << "\n";
}
此外,可用boost::diagnostic_information自动打印消息,其中包含添加到boost::exception的所有error_info对象。这对于包含在日志和其他诊断对象中很有用。
注意,实际上C++11 的标准库std::nested_exception也可以完成类似效果。
将任意数据传输到捕获点
所有派生自boost::exception的异常类型都可以用作任意数据对象的类型安全容器,当异常源自boost::exception时,可以将任意数据添加到异常对象,下面的示例演示如何使用Boost exception将errno存储在异常对象中:
#include <boost/exception/all.hpp>
#include <iostream>
typedef boost::error_info<struct tag_my_info,int> my_info; //(1)
struct my_error: virtual boost::exception, virtual std::exception { }; //(2)
void
f()
{
throw my_error() << my_info(42); //(3)
}
首先,我们使用唯一标识符tag_my_info及其标识的信息类型int来实例化error_info模板。这为存储在异常对象中的各种值提供了编译时类型安全。
tag_my_info仅仅是一个标记,通常是一个空类,不需要实现;
第二个模板参数是真正存储信息的类型。
其次,我们定义了类my_error,它派生自boost::exception。同时也派生至std::exception,多重继承可以使my_error同时获得两者的能力,由于避免菱形继承带来的二义性,需要采用虚拟继承的方式。
最后,(3)说明了如何将(1)中的typedef与运算符<<一起使用,以便在抛出点将值存储在异常对象中。
存储的my_info值可以稍后恢复,如下所示:
void
g()
{
try
{
f();
}
catch( my_error & x )
{
if( int const *mi=boost::get_error_info<my_info>(x) )
std::cerr << "My info: " << *mi;
}
}
get_error_info函数模板使用(1)中的typedef进行实例化,并传递一个多态类型的异常对象。如果异常对象包含请求的值,mi将指向该值;否则将返回空指针。
向异常添加一组数据
下面的代码片段演示了如何使用boost::tuple将失败函数的名称与报告的错误号捆绑在一起,以便更方便地将它们添加到异常对象中:
#include <boost/exception/info_tuple.hpp>
#include <boost/exception/errinfo_file_name.hpp>
#include <boost/exception/errinfo_api_function.hpp>
#include <boost/exception/errinfo_errno.hpp>
#include <boost/shared_ptr.hpp>
#include <stdio.h>
#include <string>
#include <errno.h>
typedef boost::tuple<boost::errinfo_api_function,boost::errinfo_errno> clib_failure;
struct file_open_error: virtual boost::exception { };
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() <<
boost::errinfo_file_name(name) <<
clib_failure("fopen",errno);
}
boost::errinfo_file_name是boost.exception内置一组错误信息类中的一个,还包括errinfo_at_line,errinfo_api_function,errorinfo_errno等比较实用的错误信息类,这样不用像上面定义errno_code和my_info一样自己定义错误信息类了。
请注意,boost::tuple的成员分别存储在异常对象中;只能使用get_error_info单独检索它们。
在线程之间传输异常
Boost Exception支持通过克隆在线程之间传输异常对象。由函数boost::throw_exception发出的所有异常都派生自boost::exception并支持克隆。
在抛出时使用enable_current_exception
#include <boost/exception/info.hpp>
#include <boost/exception/errinfo_errno.hpp>
#include <stdio.h>
#include <errno.h>
struct file_read_error: virtual boost::exception { };
void
file_read( FILE * f, void * buffer, size_t size )
{
if( size!=fread(buffer,1,size,f) )
throw boost::enable_current_exception(file_read_error()) <<
boost::errinfo_errno(errno);
}
克隆并重新引发异常
捕获异常时,可以调用current_exception以获取exception_ptr对象,注意current_exception只能在catch中调用:
#include <boost/exception_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
void do_work(); //throws cloning-enabled boost::exceptions
void
worker_thread( boost::exception_ptr & error )
{
try
{
do_work();
error = boost::exception_ptr();
}
catch( ... )
{
error = boost::current_exception();
}
}
在上面的示例中,请注意current_exception捕获异常对象的原始类型。可以使用rethrow_exception函数再次引发异常:
// ...continued
void
work()
{
boost::exception_ptr error;
boost::thread t( boost::bind(worker_thread,boost::ref(error)) );
t.join();
if( error )
boost::rethrow_exception(error);
}
Boost.ThrowException
头文件<boost/throw_exception.hpp>引入了boost::throw_exception函数和实用的BOOST_THROW_EXCEPTION宏。这个宏将打印发生异常的文件位置:
#include <boost/throw_exception.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include <stdexcept>
#include <iostream>
void f()
{
BOOST_THROW_EXCEPTION( std::runtime_error( "Unspecified runtime error" ) );
}
int main()
{
try
{
f();
}
catch( std::exception const & x )
{
std::cerr << boost::diagnostic_information( x ) << std::endl;
}
}
输出如下:
example.cpp(8): Throw in function void f()
Dynamic exception type: boost::wrapexcept<std::runtime_error>
std::exception::what: Unspecified runtime error