Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1040010
  • 博文数量: 264
  • 博客积分: 6005
  • 博客等级: 大校
  • 技术积分: 2798
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-08 20:15
文章分类

全部博文(264)

文章存档

2011年(42)

2010年(213)

2009年(4)

2008年(2)

2007年(3)

分类:

2010-06-11 14:26:26

在我们的项目中,可能会定义各种不同的异常,往往会遇到这种问题。比如:需要捕捉一个读取文件失败的异常 

catch( file_read_error & e )
{
std::cerr << e.file_name();
}

而我们的实现代码可能是这样的

void read_file( FILE * f )
{
....
size_t nr=fread(buf,1,count,f);
if( ferror(f) )
throw file_read_error(???);
....
}

注意file_read_error中的???,捕捉异常处需要我们显示文件名,在实际抛出异常处,我们却无法得到文件名作为参数,我们仅有的,只 是一个文件句柄FILE* !

通常折中的解决方案是,增加一个函数参数,将文件名传入,以供异常处使用

void read_file( FILE * f, char const * name )
{
....
size_t nr=fread(buf,1,count,f);
if( ferror(f) )
throw file_read_error(name);
....
}

但是,这并不是一个好的解决方案。通常,修改函数接口可能会影响较大(若这是一个虚函数接口,那么影响会更加庞大),而函数接口修改的目的,仅仅是 异常需要用特定的参数,这未免有点得不偿失。

让我们这样思考:在异常抛出与异常捕捉之间往往会跨过多个函数调用,而这中间可能会提供出我们所需要的信息,为何不充分利用这一点呢?来看看 boost中是如何解决这种异常问题的。

BOOST的异常解决方案


在boost中把异常与异常数据分开处理,让我们看看实际代码

struct exception_base: virtual std::exception, virtual boost::exception { };
struct io_error: virtual exception_base { };
struct file_read_error: virtual io_error { }; (1)

typedef boost::error_info errno_code; (2)

void read_file( FILE * f )
{
....
size_t nr=fread(buf,1,count,f);
if( ferror(f) )
throw file_read_error() << errno_code(errno); (3)
....
}

 上面的代码有几点注意的地方:

1、在(1)处我们定义了异常,尤其注意的是这些异常非常简单,不含有任何成员函数和成员变量。限制是,必须继承自 boost::exception。

2、在(2)处我们定义了异常数据,boost::error_info的第二个模板参数int,表明该异常数据为int类型,而 tag_errno_code则是一种标识(毕竟int类型的异常数据可能还有其他含义)。

3、在(3)处我们通过使用<<操作符,将异常与异常数据进行绑定。

 这里貌似并没有解决我们的文件名问题,不要着急,让我们看看更上层的代码,也就是调用read_file的地方

typedef boost::error_info file_name;  (1)
....
try
{
if( FILE * fp=fopen("foo.txt","rt") )
{
shared_ptr f(fp,fclose);
....
read_file(fp); (2)
do_something();
....
}
else
throw file_open_error() << errno_code(errno);
}
catch( boost::exception & e )
{
e << file_name("foo.txt"); (3)
throw; (4)
}

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 )
{
std::cerr << "I/O Error!\n";

if( std::string const * fn=get_error_info(e) ) (1)
std::cerr << "File name: " << *fn << "\n";

if( int const * c=get_error_info(e) )
std::cerr << "OS says: " << strerror(*c) << "\n";
}

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 __cdecl my_fopen(const char *,const char *)
Dynamic exception type: class boost::exception_detail::clone_impl
std::exception::what: example_io error
[struct tag_errno *] = 2, OS says "No such file or directory"
[struct tag_file_name *] = tmp1.txt
[struct tag_function *] = fopen
[struct tag_open_mode *] = rb

结论


使用boost的异常,可以使得异常与异常数据分离,使得程序正常流程与异常流程不再有更多的依赖。

阅读(4784) | 评论(0) | 转发(0) |
0

上一篇:虫洞

下一篇: VC中的ComBox控件详解 (转)

给主人留下些什么吧!~~