The Execution Monitor 的作用顾名思义,就是运行监视器。
根据文档的描述,这个监视器的作用就是捕捉异常,然后报告。
使用方法文档已经比较详细了。
我感兴趣的,是它的实现。
文档给出的例子是这样的:
// (C) Copyright Gennadiy Rozental 2003-2005. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org/libs/test for the library home page. #include <boost/test/execution_monitor.hpp> #include <boost/test/utils/basic_cstring/io.hpp> #include <iostream> struct my_exception1 { explicit my_exception1( int res_code ) : m_res_code( res_code ) {} int m_res_code; }; struct my_exception2 { explicit my_exception2( int res_code ) : m_res_code( res_code ) {} int m_res_code; }; namespace { class dangerous_call { public: dangerous_call( int argc ) : m_argc( argc ) {} int operator()() { // here we perform some operation under monitoring that could throw my_exception if( m_argc < 2 ) throw my_exception1( 23 ); if( m_argc > 3 ) throw my_exception2( 45 ); else if( m_argc > 2 ) throw "too many args"; return 1; } private: // Data members int m_argc; }; void translate_my_exception1( my_exception1 const& ex ) { std::cout << "Caught my_exception1(" << ex.m_res_code << ")"<< std::endl; } void translate_my_exception2( my_exception2 const& ex ) { std::cout << "Caught my_exception2(" << ex.m_res_code << ")"<< std::endl; } } // local_namespace int main( int argc , char *[] ) { ::boost::execution_monitor ex_mon; ex_mon.register_exception_translator<my_exception1>( &translate_my_exception1 ); ex_mon.register_exception_translator<my_exception2>( &translate_my_exception2 ); try { ex_mon.execute( ::boost::unit_test::callback0<int>( dangerous_call( argc ) ) ); } catch ( boost::execution_exception const& ex ) { std::cout << "Caught exception: " << ex.what() << std::endl; } return 0; } // EOF
可以看到,基本的流程就是实例化 execution_monitor , 注册自定义的异常处理函数,接着用 boost::execution_monitor::execute 执行测试对象。
先来分析一下 ex_mon.register_exception_translator<my_exception1>( &translate_my_exception1 ) ;
ex_mon 的类型是 execution_monitor ,查看 execution_monitor 的实现,可以看到 m_custom_translators 成员,其类型为boost::scoped_ptr<detail::translate_exception_base> ,是一个智能指针。而在 register_exception_translator 这个成员函数中,只有这么一句话:
m_custom_translators.reset(
new detail::translate_exception<Exception,ExceptionTranslator>( tr,m_custom_translators ) );
是对 m_custom_translators 的重新赋值。而赋值的内容,是 new 返回的 translate_exception<Exception,ExceptionTranslator>*
很明显,translate_exception<Exception,ExceptionTranslator> 必然派生自 detail::translate_exception_base , 否则这个赋值无法成立。查看translate_exception<Exception,ExceptionTranslator>,果然如此。
而 translate_exception<Exception,ExceptionTranslator> 的构造中,为什么要传入 m_custom_translators 呢? 联想到 main 里面 register_exception_translator 的用法,可以推测,需要这个参数来建立一个异常处理列表(这个说法不太准确,总之就是类似消息映射,建立异常—函数映射表)。
translate_exception<Exception,ExceptionTranslator> 的构造函数是这样的:
translate_exception( ExceptionTranslator const& tr, base_ptr& next )
: translate_exception_base( next ), m_translator( tr ) {}
参数 next 对应 m_custom_translators 。
base_ptr 的定义: typedef boost::scoped_ptr<translate_exception_base> base_ptr
接着看:
translate_exception_base( boost::scoped_ptr<translate_exception_base>& next )
{
next.swap( m_next );
}
m_next 是 translate_exception_base 的成员:boost::scoped_ptr<translate_exception_base> m_next;
将 next 赋值给 m_next , 很典型的列表操作嘛。
回过头接着分析 translate_exception( ExceptionTranslator const& tr, base_ptr& next )
: translate_exception_base( next ), m_translator( tr ) {} :
可以看到,参数 tr 被赋给了 m_translator 。结合 translate_exception_base::m_next 分析, translate_exception<Exception,ExceptionTranslator> 果然是一个列表的节点,数据成员是 m_translator ,节点指针是 m_next 。(感谢 scope_ptr ,列表头节点析构,整个列表自动析构)
到这里,就可以整理一下这个“异常映射表”的建立过程了:
template<typename Exception, typename ExceptionTranslator>
void
execution_monitor::register_exception_translator( ExceptionTranslator const& tr, boost::type<Exception>*=0 )
这个函数带入了异常类型,异常处理函数类型,异常函数三个信息( boost::type<Exception>* 用于引发函数重载,但对于这个参数的必要性,我持怀疑态度,因为我用类似的模板函数,并不需要这样的参数来引发函数重载,我用的是g++),其中异常类型是用模板参数,显式带入的。
然后,利用带入的信息,new一个异常映射表的节点,并加入映射表中(都在 translate_exception 的构造函数内完成)。
随后 execution_monitor 以m_custom_translators 记录表头节点。
这样,一个异常映射表就建立起来了。
那么,这个映射表又是如何捕捉不同的异常的呢?
查看 translate_exception_base 的定义,有这么一个纯虚函数:
virtual int operator()( unit_test::callback0<int> const& F ) = 0;
其参数的类型,一个回调对象,与 ex_mon.execute( ::boost::unit_test::callback0<int>( dangerous_call( argc ) ) 是一致的。显然,excute 这个函数,肯定要调用到这个 operator() 来执行被测试对象,捕捉其异常。
而这个函数是纯虚的,其定义在派生类中必然出现。在 translate_exception<Exception,ExceptionTranslator> 中,定义如下:
int operator()( unit_test::callback0<int> const& F )
{
try {
return m_next ? (*m_next)( F ) : F();
} catch( Exception const& e ) {
m_translator( e );
return boost::exit_exception_failure;
}
}
注意下划线部分,这是一个递归调用的写法。但是, operate() 是虚函数,“递”的实际上并不是同一个函数。这就是精妙之处了,在函数递进的过程中,不同异常的捕捉也一层层地建立了,直到最后执行被测试对象。而异常是如何抛出的呢?是根据函数调用的顺序,一层层向外抛出,直到被捕获。由此, operator() 以递归的方式,保证抛出的异常能够遍历整个“异常映射表”。这实在是一种简洁得美丽的实现。
execution_monitor 最精华的,就是这个 exception_traslator 的实现了,其他的地方基本上就是标准异常处理和系统信号处理的内容了。
局限性:excution_monitor 只能测试没有参数的被测对象,不能像 boost::signal 那样,绑定任意参数的函数