Boost Exception 官方文档学习笔记

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值