boost exception

exception:
 c++98标准定义了标准异常类std::exception及一系列子类,是整个c++语言错误处理的基础.
 boost.exception库针对标准库中异常类的缺陷进行了强化,提供<<操作符重载,可以向异常传入任意数据,有助于增加异常的信息和表达力。
#include <boost/exception/all.hpp>
using namespace boost;

标准库中的异常:
c++98标准中定义了一个异常基类std::exception和try/catch/throw异常处理机制,std::exception又派生出若干子类,用以描述不用种类的异常,如bad_alloc,bad_cast,out_of_range等等。共同构建了c++异常处理框架.
 c++允许任何类型作为异常抛出,但在std::exception出现后,我们应该尽量使用它,因为std::exception提供了一个很有用的成员函数what(),可以返回异常所携带的信息,这比简单的抛出一个整数错误值或者字符串更好,更安全;
 如果std::exception及其子类不能满足程序对异常处理的要求,也可以继承它,为它添加更多的异常诊断信息:
示例;
class my_exception : public std::exception //继承标准异常类
{
private:
 int err_no;  //错误码信息
public:
 my_exception(const char* msg, int err):     //构造函数
  std::exception(msg), err_no(err){}  //初始化父类和错误码信息
 int get_err_no()
 { return err_no;} 
};
 这种解法还存在一个问题:很多时候当发生异常时不能获得有关异常的完全诊断信息,而标准库的异常类一旦被抛出,它就成为了一个“死”对象,程序失去了对它的控制能力,只能使用它或者在抛出一个新的异常。
 c++98标准的异常处理机制还不够完美。
 下面的exception指的是boost::exception类;

exception库提供了两个类,exception和error_info,它们是exception库的基础。

exception的类摘要如下;
class exception
{
protected:
 exception();
 exception(exception const &x);
 ~exception();
 template<class E, class Tag, class T>
private:
 friend E const & operator<<(E const &, error_info<Tag, T>const &);
};
template<class ErrorInfo, class E>
 typename ErrorInfo::error_info::value_type * get_error_info(E &x);
 exception类几乎没有公开的成员函数(但有大量用于内部实现的私有函数和变量),被保护的构造函数表明了它的设计意图:它是一个抽象类,处理它的子类,任何人都不能创建或者销毁它,这保证了exception不会被误用。
 exception的重要能力在于其友元操作符<<,可以存储error_info对象信息,存入的信息可以用自由函数get_error_info<>()随时再取出来。这个函数返回一个存储数据的指针,如果exception里没有这种类型的信息则返回空指针;
 注意:exception特意没有从std::exception继承;
error_info的类摘要如下:
class error_info
{
public:
 typedef T value_type;
 error_info(value_type const &v);
 value_type & value();
};

error_info提供了向异常类型添加信息的通用解法,第一个模板类型参数Tag是一个标记,它通常是一个空类,仅用来标记error_info类,使它在模板实例化时生成不同的类型,第二个模板类型参数T是真正存储信息的数据,可以用成员函数value()访问。

向异常传递信息:
 exception和error_info被设计为配合std::exception一起工作,自定义的异常类可以安全地从exception和std::exception多重继承,从而获得两者的能力.
 因为exception被定义为抽象类,因此程序必须定义它的子类才能使用它,exception必须使用虚拟继承的方式,通常,继承完成后自定义异常类的实现就结束了:
示例:
struct my_exception:
 virtual std::exception,
 virtual boost::exception
{};    //空实现,不需要实现代码

接下来需要定义异常需要存储的信息--使用模板类error_info,用一个struct作为第一个模板参数来标记信息类型,再用第二个模板参数指定信息的数据类型;
下面的代码使用error_info定义了两个存储int和string的信息类:
typedef boost::error_info<struct tag_err_no, int> err_no;
typedef boost::error_info<struct tag_err_str,string>err_str;

当发生异常时,可以创建一个自定义异常类,并用<<操作符向它存储任意信息,这些信息可以再任何时候使用get_error_info()函数提取。
示例:
#include <boost/exception/all.hpp>
using namespace boost;
using namespace std;
struct my_exception:
 virtual std::exception,
 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;
int main()
{
 try
 {
  try
  {
   throw my_exception() << err_no(10);
  }
  catch (my_exception& e)
  {
   cout<<*get_error_info<err_no>(e)<<endl;
   cout<< e.what()<<endl;
   e<< err_str("other Info");
   throw;
  }
 }
 catch(my_exception& e)
 {
  cout<< *get_error_info<err_str>(e)<<endl;
 }
 system("pause");
 return 0;
}

 程序首先定义了一个异常类my_exception,然后使用typedef定义了两种异常信息,err_no和err_str,用int和string分别存储错误码和错误信息,mian()函数使用function-try块来捕获异常,它把整个函数都包含在try块中,可以更好地把异常处理代码与正常流程代码分离;
 throw my_exception()语句创建了一个my_exception异常类的临时对象,并立刻使用<<向它传递了err_no对象,存入错误码10,随后,异常被catch块捕获,自由函数get_error_info<err_no>(e)可以获得异常内部保存的信息值的指针,所以需要解引用操作符*访问。
 异常还可以被追加信息,同样使用操作符<<,最后在function-try的catch块部分,异常被最终处理,程序结束.

更进一步的用法:
 由于从exception派生出的异常类定义非常简单,因此可以很容易建立起一个适合自己程序的,精细完整的异常体系,使每个异常类只对应一种错误类型,使每个异常类只对应一种错误类型,只有都使用虚继承,类体系可以任意复杂,充分表达错误的含义:
 处理异常的另一个重要工作是定义错误信息类型,基本方法是使用typedef来具体化error_info模板类,这比较麻烦,特别是有大量信息类型的时候,因此exception库特意提供若干预先定义好的错误信息类,如同标准定义的logic_err等类型,使程序员用起来更轻松:
 typedef error_info<struct errinfo_api_function_, char const *>
  errinfo_api_function;
 typedef error_info<struct errinfo_at_line, int>errinfo_at_line;
 typedef error_info<struct errinfo_errno_, int> errinfo_errno;
 typedef error_info<struct errinfo_file_handle_,weak_ptr<FILE>>
  errinfo_file_handle;
 typedef error_info<struct errinfo_file_name_, std::string> errinfo_file_name;
 typedef error_info<struct errinfo_file_open_mode_,str::string>
  errinfo_file_open_mode;
 typedef error_info<struct errinfo_type_info_name, std::string>
  errinfo_type_info_name;
 可用于常见的调用api,行号,错误代码,文件handle,文件名等错误信息的处理。
例如:
 try
 {
  my_exception() << errinfo_api_function("call api")
   << errorinfo_errno(101);
 }
 catch(boost::exception& e)
 {
  cout<< *get_error_info<errinfo_api_function>(e);
  cout<< *get_error_info<errinfo_errno>(e);
 }
 另外,exception库还提供三个预定义错误信息类型,但命名规则略有不同:
 typedef error_info<struct throw_function_, char const*> throw_function;
 typedef error_info<struct throw_file_, char const*> throw_file;
 typedef error_info<struct throw_line_, int> throw_line;

 这三个错误信息类型主要用于存储源代码的信息,配合宏BOOST_CURRENT_FUNCTION,__FILE__和__LINE__使用,可以获得调用函数名,源文件名和源代码行号.
 但如果这些预定义类不能满足要求,还有使用typedef,为了解决这个不大不小的麻烦,可以自定义个辅助宏DEFINE_ERROR_INFO,它可以方便快捷地实现error_info的定义:

#define DEFINE_ERROR_INFO(type, name) \ 
 typedef boost::error_info<struct tag##name, type>name
宏DEFINE_ERROR_INFO接受两个参数,type是它要存储的类型,name是所需要的错误信息类型名,使用预处理命令##创建了error_info所需要的标签类,它的使用方法很简单,就像是声明一个变量:
DEFINE_ERROR_INFO(int, err_no); 
 在宏展开后它相当于:
typedef boost::error_info<struct tag_err_no, int> err_no;
如果担心tag_前缀太简单,可能会与其他类名发生冲突,也可以自己定制特殊的标记字符串,本身建立用内置的__FILE__和__LINE__宏,:
 tag_##__FILE__##__LINE__##name

包装标准异常:
 exception库提供一个模板函数enable_error_info<T>(T &e),其中T是标准异常类或者其他自定义类型,它可以包装类型T,产生一个从boost::exception和T派生的类,从而在不修改原异常处理体系的前提下获得boost::exception的所有好处,如果类型T已经是boost::exception的子类,那么enable_error_info将返回e的一个拷贝.
 enable_error_info()通常用在程序中已经存在异常类的场合,对这些异常类的修改很困难,这时候enable_error_info()就可以包装原有的异常类,从而很容器地在不变动任何已有代码的基础上把boost::exception集成到原有异常体系中。
示例:
#include <boost/exception/all.hpp>
using namespace boost;
using namespace std;
struct my_err{};  //某个自定义的异常类,未使用boost::exception
int main()
{
 try
 {
  //使用enable_error_info包装自定义异常
  throw enable_error_info(my_err()) << errinfo_errno(10);
 }
 catch (boost::exception& e) //这里必须使用boost::exception来捕获
 {
  cout << *get_error_info<errinfo_errno> (e) <<endl;
 }
 system("pause");
 return 0;
}
 注意代码中catch的用法,enable_error_info()返回的对象是boost::exception 和my_err的子类,catch的参数可以是这两者中的任意一个,但如果要使用boost::exception所存储的信息,就必须用boost::exception来捕获异常. 

 enable_error_info()也可以包装标准库的异常:
 throw enable_error_info(std::runtime_error("runtime"))
  << errinfo_at_line(__LINE__);//包装标准异常

使用函数抛出异常:
 由于各种原因,程序中的异常并不能总是从boost::exception继承,必须使用enable_error_info()来包装。
 exception库提供throw_exception()函数来简化enable_error_info()的调用,它可以代替原始的throw语句来抛出异常,会自动使用enable_error_info()来包装异常对象,而且支持线程安全,比直接使用throw更好,相当于:
throw (boost::enable_error_info(e))
 从而确保抛出的异常是boost::exception的子类,可以追加异常信息,例如:
throw_exception(std::runtime_error("tuntime"))

 在throw_exception()的基础上exception库又提供了一个非常有用的宏BOOST_THROW_EXCEPTION,它调用了boost::throw_exception()和enable_error_info(),因而可以接受任意的异常类型,同时又使用throw_function,throw_file和throw_line自动向异常添加了发生异常的函数名,文件名和行号等信息。
 注意,throw_exception()函数和BOOST_THROW_EXCEPTION宏都 要求参数e必须是std::exception的子类.
 如果确保在程序中总使用boost::exception,不会去定义配置宏BOOST_NO_EXCEPTIONS,那么可以修改Boost源代码,在<boost/throw_exception.hpp>里注释掉throw_exception_assert_compatibility(e)这条语句,以取消这个限制.

获取更多的调试信息:
 exception提供了方便的存储信息能力,可以向它添加任意数量的信息,但当异常对象被用operator<<多次追加数据时,会导致它存储有大量的信息(BOOST_THROW_EXCEPTION就是个例子),如果还是采用自由函数get_error_info来逐项检索的话会很麻烦,这是需要另外一个函数,diagnostic_information().
diagnostic_information()可以输出异常包含的所有信息,如果异常是由宏BOOST_THROW_EXCEPTION抛出的,则可能相当多并且不是用户友好的,但对程序开发者可以提供很多诊断错误信息。
示例;
#include <boost/exception/all.hpp>
using namespace boost;
using namespace std;
struct my_err{};  //某个自定义的异常类,未使用boost::exception
int main()
{
 try
 {
  //使用enable_error_info包装自定义异常
  throw enable_error_info(my_err()) << errinfo_errno(101)
           << errinfo_api_function("fopen");
 }
 catch (boost::exception& e) //这里必须使用boost::exception来捕获
 {
  cout << diagnostic_information(e) <<endl;
 }
 try
 {
  BOOST_THROW_EXCEPTION(std::logic_error("logic"));//必须是标准异常
 }
 catch (boost::exception& e)
 {
  cout<< diagnostic_information(e)<<endl;
 }
 system("pause");
 return 0;
}
高级议题:
 对异常信息打包:
 exception支持使用boost::tuple对异常信息进行组合打包,当异常信息类型很多有经常成组出现时可以简化抛出异常的编写:
例如:
typedef tuple<errinfo_api_function, errinfo_errno> err_group;
try
{
 //使用enable_error_info包装自定义异常
 throw enable_error_info(std::out_of_range("out"))
   << err_group("syslogd", 874);
}
catch(boost::exception&)
{
 cout<< current_exception_diagnostic_information()<<endl;
}

类型转换:
 模板函数current_exception_cast<E>()提供类似标准库的转型操作,它类似于current_exception_diagnostic_informationn(),只能在catch块内部使用,可以把异常对象转型为指向E类型的指针,如果异常对象无法转换成E*,则返回空指针。
示例:boost::exception转型为std::exception,前提是异常必须是std::exception的子类
catch( boost::exception&)
{
 cout<< current_exception_cast<std::exception>()->what();
}

线程间传递异常:
 exception库支持在线程间传递异常,这需要使用boost::exception的clone能力,使用enable_current_exception()包装异常对象或者使用throw_exception()都能够包装异常对象使之可以被clone.
 当发生异常时,线程在需要catch块调用函数current_exception()得到当前异常对象的指针,exception_ptr对象,它指向异常对象的拷贝,是线程安全的,可以被多个线程同时拥有并发修改,rethrow_exception()可以重新抛出异常.
示例:
#include <boost/exception/all.hpp>
using namespace boost;
using namespace std;
void thread_work()  //线程工作函数
{
 throw_exception(std::exception("test"));
}
int main()
{
 try
 {
  thread_work();  //启动一个线程,可能抛出异常
 }
 catch (...) //这里必须使用boost::exception来捕获
 {
  exception_ptr e = current_exception();
  cout<<current_exception_diagnostic_information();
 }
 system("pause");
 return 0;
}

你可能感兴趣的:(boost exception)