分类:
2010-06-11 14:26:26
在我们的项目中,可能会定义各种不同的异常,往往会遇到这种问题。比如:需要捕捉一个读取文件失败的异常
catch( file_read_error & e ) |
而我们的实现代码可能是这样的
void read_file( FILE * f ) |
注意file_read_error中的???,捕捉异常处需要我们显示文件名,在实际抛出异常处,我们却无法得到文件名作为参数,我们仅有的,只 是一个文件句柄FILE* !
通常折中的解决方案是,增加一个函数参数,将文件名传入,以供异常处使用
void read_file( FILE * f, char const * name ) |
但是,这并不是一个好的解决方案。通常,修改函数接口可能会影响较大(若这是一个虚函数接口,那么影响会更加庞大),而函数接口修改的目的,仅仅是 异常需要用特定的参数,这未免有点得不偿失。
让我们这样思考:在异常抛出与异常捕捉之间往往会跨过多个函数调用,而这中间可能会提供出我们所需要的信息,为何不充分利用这一点呢?来看看 boost中是如何解决这种异常问题的。
在boost中把异常与异常数据分开处理,让我们看看实际代码
struct exception_base: virtual std::exception, virtual boost::exception { }; |
上面的代码有几点注意的地方:
1、在(1)处我们定义了异常,尤其注意的是这些异常非常简单,不含有任何成员函数和成员变量。限制是,必须继承自 boost::exception。
2、在(2)处我们定义了异常数据,boost::error_info的第二个模板参数int,表明该异常数据为int类型,而 tag_errno_code则是一种标识(毕竟int类型的异常数据可能还有其他含义)。
3、在(3)处我们通过使用<<操作符,将异常与异常数据进行绑定。
这里貌似并没有解决我们的文件名问题,不要着急,让我们看看更上层的代码,也就是调用read_file的地方
typedef boost::error_info |
1、在(1)处我们定义了文件名的异常数据,可见它的数据类型为std::string,标识为tag_file_name
2、在(2)处就是我们的read_file函数,它可能会抛出file_read_error异常,记住它是继承自 boost::exception的。
3、在(3)处我们将捕捉到的所有boost::exception异常都绑定上文件名,当然也包括了file_read_error异常。这样 file_read_error异常现在已经绑定了errno和file_name2个异常数据。
4、在(4)处我们将异常重新抛出,供更上层的函数使用。
回到我们一开始的代码,捕捉异常之处,代码现在可能就是这样子:
catch( io_error & e ) |
1、在(1)处我们通过使用get_error_info函数取出我们想要的异常数据,模板参数即是需要的异常数据的类型
我们也可以使用diagnostic_information函数取得详细的诊断信息:
catch( io_error & e ) diagnostic_information(e); } |
打印出来的诊断信息,可能是这样子的:
example_io.cpp(83): Throw in function class boost::shared_ptr |
使用boost的异常,可以使得异常与异常数据分离,使得程序正常流程与异常流程不再有更多的依赖。