,今天在研究RxCpp的代码时,发现了下面的函数on_exception:

用途是运行函数f,当出现错误时使用c进行错误处理,相当于直接写下面的try…catch段.

这里有两个问题:

1.它是如何检验onError是它需要的错误处理函数的?

2.为什么要使用模板检验onError的类型,而不是直接在参数处限定类型呢?

 

对于问题1,它是通过函数返回值的类型推导,来对 onError 的类型进行限定的.首先从下面的catch代码,可以发现 onError 需要的函数格式为 void(std::exception_ptr) .在上面的代码中,唯一与这个格式相关的,是 is_on_error::check 函数.这个函数有两个定义,一个是 int 参数的定义,另一个是任意参数的定义. int 参数的定义通过 decltype 推导出了该函数在通常调用时的返回值(同时检验了参数是否吻合),之后再通过 std::is_same<T,U> 检验返回值是否为 void ,得出结果.根据C++模板推导的特性,如果带入后推导出的函数的类型是无效的,就放弃这种偏特化方式(尝试其他的方式,如果没有则报错,单纯一种推导失败并不会报错),当int的定义推导失败时,就会采用第二种定义,其返回值显然不是 void , is_on_error::value 显然也为 false 了.

在检验之后,又通过 std::enable_if<B,T> ,当第一个模板参数为false时会输出无效类型, 使函数返回值的有效性与检验的结果连接在了一起.

 

而问题2才是使用这种方法获得的主要好处,1只是实现的手段之一.该函数通过使用模板机制,使得参数可以接受的函数类型增加了(无论是对于t,还是c).这个效果主要分为两个方面:

(1)std::decay<T>

这个函数可以将形如 int(int) 的函数类型,转换为 int(*)(int) ,后者则不做任何改变.同时可以移除输入类型的&,&&, const 等信息,方便后续比较.通过 std::decay<T> ,将这个函数运行的输入扩展了一种.

(2) decltype 推导

可以注意到,上面通过对CF类型的函数调用,来获取到函数的返回值类型,而能通过()形式进行调用的,并不是只有函数的,还有 operator() .也就是说可以接受所有拥有 operator()(std::exception_ptr) 重载的对象!举一个比较普遍的例子,这个函数可以接受 std::function<R(Args…)> 对象, 也就是说可以方便地使用 std::bind<R(Args…)> 包装出函数了.

 

 

以上是今天对RxCpp库的研究记录,如果有什么错误,或疑问,欢迎评论进行讨论.

*PS:差点被”明明参数限定类型就好了为什么要用模板呢”问倒T T

*PS2:那么 is_on_error::check 的调用算不算重载呢…?