boost bind初步探究
Posted xuhao_xuhao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了boost bind初步探究相关的知识,希望对你有一定的参考价值。
最近对boost的bind部分比较感兴趣,对其背后的机制进行了简单的分析,和大家分享一下。
注,我所看的代码是boost_1_51_0, 想来各个版本的差异不大。
从一个例子开始
直接上代码(从官方网站摘取)
定义函数
[cpp] view plain copy print ?- int f(int a, int b)
- return a + b;
- int g(int a, int b, int c)
- return a + b + c;
int f(int a, int b)
return a + b;
int g(int a, int b, int c)
return a + b + c;
调用范例:
bind(f, 1, 2)(); //f(1,2)
bind(f, _2, _1)(x, y); // f(y, x)
bind(g, _1, 9, _1)(x); // g(x, 9, x)
bind(g, _3, _3, _3)(x, y, z); // g(z, z, z)
bind(g, _1, _1, _1)(x, y, z); // g(x, x, x)
_1, _2, ... _9在 boost中被称为placeholder,是占位符的意思。它表示参数。这种方式,我是只在boost中见过,是个非常神奇的用法。
它们究竟是什么呢?,且看定义:(boost/bind/placeholders.hpp)
[cpp] view plain copy print ?- boost::arg<1> _1;
- boost::arg<2> _2;
- boost::arg<3> _3;
- boost::arg<4> _4;
- boost::arg<5> _5;
- boost::arg<6> _6;
- boost::arg<7> _7;
- boost::arg<8> _8;
- boost::arg<9> _9;
boost::arg<1> _1;
boost::arg<2> _2;
boost::arg<3> _3;
boost::arg<4> _4;
boost::arg<5> _5;
boost::arg<6> _6;
boost::arg<7> _7;
boost::arg<8> _8;
boost::arg<9> _9;
boost::arg也是个模板,至于是什么样的模板,留个悬念吧。
boost bind的这些功能,颠覆了我对C++的看法,从未想到过,C++还可以这么玩。那么,boost究竟是怎么实现的呢?
读者请注意,bind在这里涉及了两个参数表。第一个参数表是被bind绑定的函数(例子中f,g函数)的参数表,另外一个是bind生成的新的函数对象的参数表。
这两个参数表如何实现?如何转换是我们后面分析的重点。
bind是什么?
bind是函数,是非常神奇的函数,不是一个函数,而是一组函数,是一组重载的函数。
翻开代码 boost/bind/bind.hpp,找到BOOST_BIND字符串,大约在1290行的位置,boost定义了bind(1.51.0):
[cpp] view plain copy print ?- // bind
- #ifndef BOOST_BIND
- #define BOOST_BIND bind
- #endif
- // generic function objects
- template<class R, class F>
- _bi::bind_t<R, F, _bi::list0>
- BOOST_BIND(F f)
- typedef _bi::list0 list_type;
- return _bi::bind_t<R, F, list_type> (f, list_type());
- template<class R, class F, class A1>
- _bi::bind_t<R, F, typename _bi::list_av_1<A1>::type>
- BOOST_BIND(F f, A1 a1)
- typedef typename _bi::list_av_1<A1>::type list_type;
- return _bi::bind_t<R, F, list_type> (f, list_type(a1));
- template<class R, class F, class A1, class A2>
- _bi::bind_t<R, F, typename _bi::list_av_2<A1, A2>::type>
- BOOST_BIND(F f, A1 a1, A2 a2)
- typedef typename _bi::list_av_2<A1, A2>::type list_type;
- return _bi::bind_t<R, F, list_type> (f, list_type(a1, a2));
- ....
// bind
#ifndef BOOST_BIND
#define BOOST_BIND bind
#endif
// generic function objects
template<class R, class F>
_bi::bind_t<R, F, _bi::list0>
BOOST_BIND(F f)
typedef _bi::list0 list_type;
return _bi::bind_t<R, F, list_type> (f, list_type());
template<class R, class F, class A1>
_bi::bind_t<R, F, typename _bi::list_av_1<A1>::type>
BOOST_BIND(F f, A1 a1)
typedef typename _bi::list_av_1<A1>::type list_type;
return _bi::bind_t<R, F, list_type> (f, list_type(a1));
template<class R, class F, class A1, class A2>
_bi::bind_t<R, F, typename _bi::list_av_2<A1, A2>::type>
BOOST_BIND(F f, A1 a1, A2 a2)
typedef typename _bi::list_av_2<A1, A2>::type list_type;
return _bi::bind_t<R, F, list_type> (f, list_type(a1, a2));
....
太多了,只贴3个,足以说明问题。
模板参数
- R 表示返回类型
- F 表示函数指针的类型
- A1,A2, .... 这些都是参数的类型
bind函数非常简单,它其实就是返回一个bind_t类的对象。bind_t类也是一个模板类。 如果仔细观察,你可以发现,这些不同参数的bind函数,其实现上不同在于 list_av_N这一系列的辅助类是不同的。list_av_1表示一个参数,list_av_2表示两个参数, 以此类推。
这里的list_av_N对象,是第一个参数表,是函数F要求的参数表,这个参数表是可以包含placeholder的。
list_av_N对象其实只是一个包装对象。我们以list_av_2为例: [cpp] view plain copy print ?
- template<class A1, class A2> struct list_av_2
- typedef typename add_value<A1>::type B1;
- typedef typename add_value<A2>::type B2;
- typedef list2<B1, B2> type;
- ;
template<class A1, class A2> struct list_av_2
typedef typename add_value<A1>::type B1;
typedef typename add_value<A2>::type B2;
typedef list2<B1, B2> type;
;
list_av_2的作用,仅仅是为了包装而已。list_av_2::type == ist2<A1,A2> == list_type。
bind_t是什么东东?
奥秘在bind_t中,且看bind_t的定义 (也在boost/bind/bind.hpp中。下面只是bind_t的一种定义方法,但是道理都是一样的) [html] view plain copy print ?- template<class R, class F, class L> class bind_t
- public:
- typedef bind_t this_type;
- bind_t(F f, L const & l): f_(f), l_(l)
- #define BOOST_BIND_RETURN return
- #include <boost/bind/bind_template.hpp>
- #undef BOOST_BIND_RETURN
- ;
template<class R, class F, class L> class bind_t
public:
typedef bind_t this_type;
bind_t(F f, L const & l): f_(f), l_(l)
#define BOOST_BIND_RETURN return
#include <boost/bind/bind_template.hpp>
#undef BOOST_BIND_RETURN
;
模板参数R代表return type, F代表function type, L表示的是listN(list0,list1,list2,....),这个是关键啊。
至于bind_template.hpp,这个源代码也比较简单,主要是定义了operator () 的实现。
bind_t重载括号运算符,因此,bind_t可以像函数那样调用。而且, bind_t的operator()有N多个重载,分别对应的是不同的参数类型和参数个数。这使得我们可以用不同的参数调用bind_t的对象。
我们摘抄一个有一两个参数的括号重载,看看 [cpp] view plain copy print ?
- .....
- template<class A1> result_type operator()(A1 & a1)
- list1<A1 &> a(a1);
- BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
- ....
- template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)
- list2<A1 &, A2 &> a(a1, a2);
- BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
- ......
.....
template<class A1> result_type operator()(A1 & a1)
list1<A1 &> a(a1);
BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
....
template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)
list2<A1 &, A2 &> a(a1, a2);
BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
......
f_就是函数指针,这个不用多说;l_是 L (listN)对象。
请注意,这里有两个listN出现:
- 一个是l_ 这是针对f_提供的参数列表,其中包含_1,_2,....这样的placeholder
- 一个是生成的临时变量 a, 这个是bind_t函数对象在调用时 的参数列表
listN的奥秘
bind_t对象的operator () 调用的是listN 的operator (),那么,整个实现,就在listN中,为了方便说明,我们以list2为例。对list2的分析,我们只看3部分: 1. 类声明部分: [cpp] view plain copy print ?
- template< class A1, class A2 > class list2: private storage2< A1, A2 >
- private:
- typedef storage2< A1, A2 > base_type;
- public:
- list2( A1 a1, A2 a2 ): base_type( a1, a2 )
template< class A1, class A2 > class list2: private storage2< A1, A2 >
private:
typedef storage2< A1, A2 > base_type;
public:
list2( A1 a1, A2 a2 ): base_type( a1, a2 )
从这个定义,我们知道,它从storage2继承,storage2是什么?
[cpp]
view plain
copy
print
?
- template<class A1, class A2> struct storage2: public storage1<A1>
- typedef storage1<A1> inherited;
- storage2( A1 a1, A2 a2 ): storage1<A1>( a1 ), a2_( a2 )
- template<class V> void accept(V & v) const
- inherited::accept(v);
- BOOST_BIND_VISIT_EACH(v, a2_, 0);
- A2 a2_;
- ;
template<class A1, class A2> struct storage2: public storage1<A1>
typedef storage1<A1> inherited;
storage2( A1 a1, A2 a2 ): storage1<A1>( a1 ), a2_( a2 )
template<class V> void accept(V & v) const
inherited::accept(v);
BOOST_BIND_VISIT_EACH(v, a2_, 0);
A2 a2_;
;
从名字和定义上,我们就可以断定:storage2就是保存两个参数的参数列表对象。看来,storageN负责存储,而listN负责如何使用这些参数了。
2. operator[] 系列重载函数 [cpp] view plain copy print ?
- A1 operator[] (boost::arg<1>) const return base_type::a1_;
- A2 operator[] (boost::arg<2>) const return base_type::a2_;
- .....
- template<class T> T & operator[] (_bi::value<T> & v) const return v.get();
- .....
A1 operator[] (boost::arg<1>) const return base_type::a1_;
A2 operator[] (boost::arg<2>) const return base_type::a2_;
.....
template<class T> T & operator[] (_bi::value<T> & v) const return v.get();
.....
我已经剔除了一些定义,只留下我们关系的定义。
这里面有两类定义,
- 针对 boost::arg<1>和boost::arg<2>定义的。其实就是针对_1, _2的定义,这个定义表明:如果是_1,那么,list2就返回存储的参数a1, 如果是_2,那么就返回存储的参数a2。这些参数,是上面我说的针对f_函数的参数;
- 针对_bi::value<T>的定义。_bi::value<T>就是对T进行简单封装的类型。这个定义仅仅是将value的值再取出来。
3. operator()系列重载函数 [html] view plain copy print ?
- ....
- template<class F, class A> void operator()(type<void>, F & f, A & a, int)
- unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);
- ....
....
template<class F, class A> void operator()(type<void>, F & f, A & a, int)
unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);
....
尽管有多种不同的重载,但是基本形式就是这样的。 最后一个参数"int"我想没有直接的意义,可能是为了重载时用于区分不同的重载函数使用的。
unwrap的作用这里可以忽略。你可以认为就是直接调用f。
下面有两个不同寻常的语句: [html] view plain copy print ?
- a[base_type::a1_], a[base_type::a2_]
a[base_type::a1_], a[base_type::a2_]
a是一个listN对象,这两句究竟是什么意思呢?
下面,我们用两个例子分别说明, 例子1
bind(f, 1, 2)(); //f(1,2)
下面,我们将参数代入:这种情况下,我们得到的bind_t对象,实际上是
[cpp]
view plain
copy
print
?
- bind_t<int, int(*)(int,int), list2<int,int> >
- //l_的类型和值
- list2<int,int> = [ base_type::a1_ = 1, base_type::a2_ = 2]
bind_t<int, int(*)(int,int), list2<int,int> >
//l_的类型和值
list2<int,int> = [ base_type::a1_ = 1, base_type::a2_ = 2]
而bind(f, 1, 2)
(); 中最后一个括号,调用了bind_t的
operator () (void) :
[cpp]
view plain
copy
print
?
- result_type operator()()
- list0 a;
- BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
result_type operator()()
list0 a;
BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
因此,a = list0。
在这种情况下,
[cpp]
view plain
copy
print
?
- a[base_type::a1_] = =
- a.operator[]((_bi::value<int>) 1)
- == 1;
- a[base_type::a2_] ==
- a.operator[](_bi::value<int>) 2)
- == 2;
a[base_type::a1_] = =
a.operator[]((_bi::value<int>) 1)
== 1;
a[base_type::a2_] ==
a.operator[](_bi::value<int>) 2)
== 2;
其实listN里面的operator[](_bi::value<T>)就是打酱油的,目的就是为了在语法上统一。
因此,bind(f, 1, 2) () === f(1,2)的调用。
例子2
bind(f, _2, _1)(x, y); // f(y, x)我们再把参数代入,这是,得到的bind_t对象就是 [html] view plain copy print ?
- bind_t<int, int(*)(int, int), list2<boost::arg<2>, boost::arg<1> > >
- //l_的类型和值是
- list2<boost::arg<2>,boost::arg<1> > = [ base_type::a1_ = _2, base_type::a2_ = _1]
bind_t<int, int(*)(int, int), list2<boost::arg<2>, boost::arg<1> > >
//l_的类型和值是
list2<boost::arg<2>,boost::arg<1> > = [ base_type::a1_ = _2, base_type::a2_ = _1]
哈哈,看到了吧,list2的类型不一定要和F的参数类型一样的哦。
当调用bind(f, _2, _1) (x, y); 中bind_t::operator()(int, int) 的时候,即 [cpp] view plain copy print ?
- template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)
- list2<A1 &, A2 &> a(a1, a2);
- BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)
list2<A1 &, A2 &> a(a1, a2);
BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
此时,a的类型和值是
[cpp]
view plain
copy
print
?
- list2<int, int> = [ base_type::a1_= x, base_type::a2_ = y]
list2<int, int> = [ base_type::a1_= x, base_type::a2_ = y]
这种情况下,
[html]
view plain
copy
print
?
- a[l_.base_type::a1_] ==
- a [ _2 ] ==
- a.operator[] ( (boost::arg<2>&)_2) ==
- a.base_type::a2_ ==
- y;
- a[l_.base_type::a2_] ==
- a [ _1 ] ==
- a.operator[] ( (boost::arg<1>&)_1) ==
- a.base_type::a1_ ==
- x;
a[l_.base_type::a1_] ==
a [ _2 ] ==
a.operator[] ( (boost::arg<2>&)_2) ==
a.base_type::a2_ ==
y;
a[l_.base_type::a2_] ==
a [ _1 ] ==
a.operator[] ( (boost::arg<1>&)_1) ==
a.base_type::a1_ ==
x;
即 bind(f, _2, _1)(x, y) === f(y, x);
关于_1,_2,_3,...._9
现在,我们要看看,到底 boost::arg<1>, boost::arg<2> ... boost::arg<9>是什么了。 [cpp] view plain copy print ?- template< int I > struct arg
- arg()
- template< class T > arg( T const & /* t */ )
- // static assert I == is_placeholder<T>::value
- typedef char T_must_be_placeholder[ I == is_placeholder<T>::value? 1: -1 ];
- ;
template< int I > struct arg
arg()
template< class T > arg( T const & /* t */ )
// static assert I == is_placeholder<T>::value
typedef char T_must_be_placeholder[ I == is_placeholder<T>::value? 1: -1 ];
;
呵呵,什么都没有!的确,什么都没有,因为它是placheholder嘛! 它只要表明自己的类型就可以了。这是为什么在 listN的operator[] 中,boost::arg<N> 没有定义形惨了。
第二个带有 T const & 参数的构造函数是什么?看起来很奇怪,其实,它是拷贝构造函数。
看看is_placeholder的定义吧: [cpp] view plain copy print ?
- template< int I > struct is_placeholder< arg<I> >
- enum _vt value = I ;
- ;
template< int I > struct is_placeholder< arg<I> >
enum _vt value = I ;
;
它的作用,是防止错误的参考构造。 假如,你这样定义: [cpp] view plain copy print ?
- boost::arg<2> arg2(_1);
boost::arg<2> arg2(_1);
模板展开后,将是这样的
[cpp]
view plain
copy
print
?
- struct arg <2>
- .....
- arg( arg<1> const & /* t */ )
- // static assert I == is_placeholder<T>::value
- typedef char T_must_be_placeholder[ I == is_placeholder<arg<1> >::value? 1: -1 ];
- ;
struct arg <2>
.....
arg( arg<1> const & /* t */ )
// static assert I == is_placeholder<T>::value
typedef char T_must_be_placeholder[ I == is_placeholder<arg<1> >::value? 1: -1 ];
;
is_placeholder<arg<1> >::value == 1,而I == 2,因此,你会得到一个
[cpp] view plain copy print ?
- typedef char T_must_be_placeholder[ -1 ];
typedef char T_must_be_placeholder[ -1 ];
因此,你将收到一个编译错误。仅此而已。
其他
到此为止,boost bind的关键部分就已经清楚了。boost还有些高级议题,如类的成员函数的绑定、变量引用、绑定嵌套等等。这些议题都是以此为基础,再增加了一些新模板参数而已,已经不是实现的核心了,有兴趣的同学可以自己看看。以上是关于boost bind初步探究的主要内容,如果未能解决你的问题,请参考以下文章
以boost::function和boost:bind取代虚函数