c++阉割版binder实现

来源:互联网 发布:广州java 外包公司 编辑:程序博客网 时间:2024/06/05 10:22

首先分析下binder的原理:为一个仿函数绑定几个参数,或者用place holder改变参数的顺序。首先实现不带有place holder版本的binder,实现如下功能:一个binder类,构造的时候为仿函数按函数参数顺序绑定若干个函数,重载operator(),输入若干参数,内部调用仿函数,返回值为仿函数的返回值。根据功能分析可知,需要使用模板,因为绑定参数类型不一,可能需要一个tuple存储绑定的参数。如下代码所示:

template <typename Func, typename RET, typename... Args>class binder {public:binder(Func f1, Args... rest) :f(f1), t(rest...){}template <typename... Rest>RET operator ()(const Rest&... rest) ;private:Func f;tuple<Args...> t;};


重要的是实现重载的operator(),传入的参数个数可以用sizeof操作符得到,形如sizeof...(Args)。这个函数想要返回f(get<Nx>(t)...,rest...),想要得到这个序列可以通过一个辅助类和函数模板推导得到,代码比较直观(可以通过查看标准库binder 的声明,一步一步转到定义查看)

template <typename T,T... Args>class Sequence {public:typedef Sequence<T, Args...> type;typedef Tvalue_type;};


Sequence类比较简单,只有两个typedef,很简单一看就明白,这个类的作用就是通过为一个函数传入一个Sequence 让编译器推导出Args...序列。要想实现这个功能还需要一个辅助类,如下:


template <bool b,size_t... args>class Make_Seq {};template< size_t n,size_t... Args>class Make_Seq<false,n,Args...> :publicMake_Seq<n <= 1,n - 1,n,Args...>{};template<size_t... Args>class Make_Seq<true,0,Args...>:publicSequence<size_t,Args...>{};

这个类实现比较巧妙,这是一个继承链,最顶层的基类是Sequence,主要实现部分是中间那部分代码,从代码可以看出,从继承链的最底层往上,每增加一个继承,Args...就会增加一个n,即当前继承的层数。有了这样两个辅助类就可以让编译器推导出参数,实现如下:

template <size_t... args>void func(const Sequence& s) {}func(Make_Seq(false, 10));


例子中就会推导出args... = 1,2...10

有了这个辅助类,就可以实现operater()了,首先要写一个函数用来参数推导,这个函数应该直接返回仿函数的结果,因为想要将重载operater()中的不定参数传入这个函数中,因为函数模板推导中不能有两个不定参数推导,所以应当把operator()中的参数以一个tuple传入函数中,这样也面临前面同样的问题。我想到的方法是,将要binder的那些参数组成的tuple当作第一个参数和operator()需要的参数一起放到一个tuple内,并且为tulple的get函数写一个重载,当传入参数不是tulple时,直接返回传入的参数,实现如下:


template <int N, typename T>inlineTget(T&& a) {return a;}

这个就是重载的get,传入tuple的get和tuple实现有关在这就不讨论了。下面说一下怎么实现,首先在重载函数体内构造一个tuple:


tuple<tuple<Args...>, Rest...> t_arry(t, rest...);

第一个参数为一个tuple即binder的参数,后面是重载函数的参数。我们想要这样实现,对于仿函数f,想要返回f(get<Ix>(get<Nx>(t_arry))...),其中比较重要的是Nx的实现,在没有place holder的情况下,当Ix不大于binder的参数个数时,Nx为1,反之,Nx 等于 Ix 减去binder的参数个数,并且这个参数要在编译期确定,借助下面的模板实现:


template<bool C,int N1,int N2>class NSwitch {};template< int N1, int N2>class NSwitch<false,N1,N2> {public:enum {N = N2};};template< int N1, int N2>class NSwitch<true , N1, N2> {public:enum { N = N1 };};

 设绑定的参数个数为 n1,总共传入仿函数的参数个数为n2,借助上面的类实现如下:


NSwitch<n1 > Ix,1,0>::N * (Ix - n1)

binder完整实现如下:


template <typename Func, typename RET, typename... Args>class binder {public:binder(Func f1,Args... rest) :f(f1) ,t(rest...)) {}template <typename... Rest>RET operator ()(const Rest&... rest) {tuple<tuple<Args...>, Rest...> t_arry(t, rest...);return _Call<RET, sizeof...(Args)>(t, t_arry, Make_Seq<false, NSwitch<TestT<Args...>::N,sizeof...(Args),sizeof...(Args)+sizeof...(Rest)>::N>(), f);}private:Func f;tuple<Args...> t;};

_Call的实现如下:


template <typename RET,int size,typename Func,typename T1,typename T2,size_t... Ix>RET _Call(T1& t1,T2& t2,Sequence<size_t, Ix...>& s,Func f) {return f(get<Ix>(get<1 +  NSwitch<Ix > size,1,0>::N * (Ix -size)>(t2))...);}

这样就实现了没有place holder的binder,带有place holder 的实现也类似,只需要加几个辅助模板类,有空了再写吧







原创粉丝点击