阅读<<Boost程序库完全开放指南>> 第5版记录
标准库中的异常 std::exception
可以使用 成员函数 what(),获取错误信息。但在异常抛出后无法对其增加错误信息,只能使用它,或再抛出一个新的异常。
boost.exception完善了这个缺陷。
boost.exception 库
boost.exception 库 提供了两个类:exception 和 error_info。
类摘要如下:
class exception
{
protected:
exception();
exception(exception const &x);
virtual ~exception();
private:
template<class E,class tag,class T>
friend E const& operator<<(E const& ,error_info<Tag,T> const&);
};
typename value_type* get_error_info(E &x);
template<class tag,class T>
class error_info
{
public:
typedef T value_type;
error_info(value_type const &v);
value_type& value();
}
exception类的 构造函数是保护类型的,表面它的意图是 它是一个抽象类,除了它的子类,任何人都不能创建或者销毁它,这保证了 exception不会被误用。
exception的重要能力在于 其友元操作符 << ,它可以存储 error_info 对象的信息,存入的信息可以用自由函数 get_error_info随时再取出来。
error_info提供了向异常添加信息的通用解。第一个模板类型参数Tag是一个标记,它通常(最好)是一个空类,仅用来标记error_info类,使它在模板实例化时生成不同的类型。第二个模板类型参数 T 是 真正存储信息的数据,可以通过 value() 访问。
用法
exception 和 error_info 被设计为配合 std::exception 一起使用,自定义的异常类可以安全的从exception和std::exception多重继承,从而获得两者的能力。
#include <boost/exception/all.hpp>
using namespace boost;
struct my_exception :
virtual std::exception,//虚继承,struct 默认是 public继承
virtual boost::exception
{};//空实现,不需要额外代码
//定义两个错误信息类
typedef boost::error_info<struct tag_err_no, int> err_no;
typedef boost::error_info<struct tag_err_str, string> err_str;
void case1()
{
try
{
try
{
throw my_exception() << err_no(10);
}
catch (my_exception& e)
{
cout << *get_error_info<err_no>(e) << endl;// 0
cout << e.what() << endl;//Unknown exception
e << err_str("other info");
throw;
}
}
catch (my_exception& e)
{
cout << *get_error_info<err_str>(e) << endl;//other info
}
}
错误信息类
定义错误信息类比较麻烦typedef boost::error_info<struct tag_err_no, int> err_no; 所以exception库提供了一些预先定义好的错误信息类
- errinfo_api_function
- errinfo_at_line
- errinfo_errno
- errinfo_file_name
- errinfo_file_open_mode
- throw_function
- throw_file
- throw_line
也可以自定义宏 DEFINE_ERROR_INFO 来简化代码
#define DEFINE_ERROR_INFO(type, name) \
typedef boost::error_info<struct tag_##name, type> name
void case2()
{
try
{
throw my_exception() << errinfo_api_function("call api")
<< errinfo_errno(101) << throw_line(__LINE__) << errinfo_at_line(__LINE__);
}
catch (boost::exception& e)
{
cout << *get_error_info<errinfo_api_function>(e) << endl;
cout << *get_error_info<errinfo_errno>(e) << endl;
cout << *get_error_info<throw_line>(e) << endl;
cout << *get_error_info<errinfo_at_line>(e) << endl;
cout << endl;
}
}
包装标准异常
exception 库提供模板函数 enable_error_info(T &e),其中T是标准异常类 或 其他自定义类型。它可以包装类型 T,产生一个从 boost.exception 和 T 派生的类,从而在不修改 T 的前提下获得 boost::exception的所有好处。如果 T 是 boost::exception的子类,那么将返回 e的一个拷贝。
示范:
struct my_err {};
void case3()
{
try
{
throw enable_error_info(my_err()) << errinfo_errno(10);
}
catch (boost::exception& e)
{
cout << *get_error_info<errinfo_errno>(e) << endl;
}
try
{
throw enable_error_info(std::runtime_error("runtime"))
<< errinfo_at_line(__LINE__);
}
catch (boost::exception& e)
{
cout << *get_error_info<errinfo_at_line>(e) << endl;
}
}
注意catch的用法,enable_error_info() 返回的对象是 boost::exception 和 my_err(T)的子类,catch的参数可以是这两者中的任意一个,但如果要使用 boost::exception 所存储的信息,就必须要使用 boost::exception 来捕获异常。
使用函数函数抛出异常
exception库 提供了 throw_exception()函数来简化 enable_error_info()的调用,它可以替代原始的throw 语句来抛出异常,会自动调用 enable_error_info() 来包装异常对象, 而且线程安全,比直接使用throw更好,相当于throw (boost::enable_error_info(e)),从而确保抛出的异常时 boost::exception的子类,可以追加信息,例如:throw_exception(std::runtime_error("runtime"))。
在throw_exception()的基础上,exception库又提供了一个非常有用的宏,BOOST_THROW_EXCEPTION,它调用了boost::throw_exception()和enable_error_info(),因而可以接收任意的异常类型,同时又使用 throw_function、throw_file、throw_line自动向异常添加了发生异常的函数名、文件名、行号等信息。
需要注意,为了保证与配置宏 BOOST_NO_EXCEPTION兼容, throw_exception()和BOOST_THROW_EXCEPTION宏都要求参数e是 std::exception的子类。
如果确保在程序中总使用 boost::exception,不会去定义配置宏BOOST_NO_EXCEPTION(大多数是这样的),那么可以修改 boost源代码,在< boost/throw_exception.hpp>里注释掉 throw_exception_assert_compatibility(e) 这条语句,以取消限制。
获取更多信息
当异常对象被调用多次 operator<< 多次追加数据时,会导致它存储大量的信息,如果还是使用 get_error_info来逐项检索,很麻烦,所以boost库提供了 diagnostic_information(),可以输出异常包含的所有信息。
还有一个更方便的函数current_exception_diagnostic_information(),它只能在catch块内使用,这免去了指定异常参数的麻烦。
#include <boost/throw_exception.hpp>
void case4()
{
try
{
throw enable_error_info(my_err())
<< errinfo_errno(101)
<< errinfo_api_function("fopen");
}
catch (boost::exception& e)
{
cout << diagnostic_information(e) << endl;
cout << current_exception_diagnostic_information() << endl;
}
try
{
BOOST_THROW_EXCEPTION(std::logic_error("logic"));
}
catch (boost::exception& e)
{
cout << diagnostic_information(e) << endl;
}
}
//Throw location unknown (consider using BOOST_THROW_EXCEPTION)
//Dynamic exception type: struct boost::exception_detail::error_info_injector<struct my_err>
//[struct boost::errinfo_api_function_ *] = fopen
//[struct boost::errinfo_errno_ *] = 101, "address not available"
//Throw location unknown (consider using BOOST_THROW_EXCEPTION)
//Dynamic exception type: struct boost::exception_detail::error_info_injector<struct my_err>
//[struct boost::errinfo_api_function_ *] = fopen
//[struct boost::errinfo_errno_ *] = 101, "address not available"
//D:\Code\boost_learm\boost_learm.cpp(122): Throw in function void __cdecl case4(void)
//Dynamic exception type: struct boost::wrapexcept<class std::logic_error>
//std::exception::what: logic
高级议题
对异常信息打包
exception支持使用 boost::tuple 对异常信息进行组合打包。
typedef tuple<errinfo_api_function,errinfo_errno> err_group;
try
{
throw enable_error_info(std::out_of_range(out))<<err_group("syslogd",875);
}
catch(boost::exception)
{
cout << current_exception_diagnostic_information() << endl;
}
类型转换
模板函数 current_exception_cast< E>()
catch(boost::exception e)
{
current_exception_cast< std::exception>(e)->what();
}
线程间传递
exception库支持在线程间传递异常,这需要使用boost::exception 的 clone能力。使用enable_error_info() 包装异常对象 或 使用 throw_exception()都能够包装异常对象 使之可以被clone。
当发生异常时,线程需要 catch块调用current_exception()得到当前异常对象的指针对象exception_ptr
,它指向异常对象的拷贝,是线程安全的,可以被多个线程同时拥有,并发修改。rethrow_exception()可以重新抛出异常。
void thread_work()
{throw_exception(std::exception("test"));}
int main()
{
try{
thread_work();
}
catch(...)
{
exception_ptr e = current_exception();
cout << current_exception_diagnostic_information() ;
}
}
5168

被折叠的 条评论
为什么被折叠?



