boost 实用工具类4—exception

阅读<<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() ;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值