4.9.4 更进一步的用法
通过例子我们基本了解了exception的用法,可以看到exception的使用是相当清晰简单的,同时又提供了灵活强大的功能,使异常类有了更多的用途。
由于从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_,std::string>
- errinfo_file_open_mode;
- typedef error_info<struct errinfo_type_info_name_,std::string>
- errinfo_type_info_name;
它们可以用于常见的调用API、行号、错误代码、文件handle、文件名等错误信息的处理。例如: - try
- {
- throw my_exception() << errinfo_api_function("call api")
- << errinfo_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(参见4.12.2节,第158页)、__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
这几乎不会造成任何可能的冲突。