寫在前面的話
在C++11之後,std::bind是C++标準庫的一個元件了。一開始想弄個C++11的實作來研究下,發現裡面用到了可變參數模闆(代碼變得非常神奇).
http://llvm.org/svn/llvm-project/libcxx/trunk/include/functional還是弄個原始點的boost的實作來研究下。
話說網上關于boost::bind的實作的文章也有不少,不過大多數都是貼一段代碼,再扯一通,結果到頭來什麼都沒看明白。(起碼LZ是。。)
花了一天的功夫,最終從boost::bind的源代碼中摳出了可編繹運作的代碼。
下面一步一步來解析boost::bind的實作。
标準庫中的fouctor和bind1st的實作
首先從簡單的入手。先看下标準庫中的fouctor和bind1st的實作(為了防止和标準庫的命名沖突,全部都在命名後面加了2)。
#include <iostream>
#include <algorithm>
using namespace std;
template<typename _Arg1, typename _Arg2, typename _Result>
struct binary_function2 {
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
template<typename _Tp>
struct equal_to2: public binary_function2<_Tp, _Tp, bool> {
bool operator()(const _Tp& __x, const _Tp& __y) const {
return __x == __y;
}
};
template<typename _Arg, typename _Result>
struct unary_function2 {
typedef _Arg argument_type;
typedef _Result result_type;
};
template<typename _Operation>
class binder1st2: public unary_function2<
typename _Operation::second_argument_type,
typename _Operation::result_type> {
protected:
_Operation op;
typename _Operation::first_argument_type value;
public:
binder1st2(const _Operation& __x,
const typename _Operation::first_argument_type& __y) :
op(__x), value(__y) {
}
typename _Operation::result_type operator()(
const typename _Operation::second_argument_type& __x) const {
return op(value, __x);
}
typename _Operation::result_type operator()(
typename _Operation::second_argument_type& __x) const {
return op(value, __x);
}
};
template<typename _Operation, typename _Tp>
inline binder1st2<_Operation> bind1st2(const _Operation& __fn, const _Tp& __x) {
typedef typename _Operation::first_argument_type _Arg1_type;
return binder1st2<_Operation>(__fn, _Arg1_type(__x));
}
int main() {
binder1st2<equal_to2<int> > equal_to_10(equal_to2<int>(), 10);
int numbers[] = { 10, 20, 30, 40, 50, 10 };
int cx;
cx = std::count_if(numbers, numbers + 6, bind1st(equal_to2<int>(), 10));
cout << "There are " << cx << " elements that are equal to 10.\n";
}
上面的實作還是比較簡單的,希望還沒有看暈。:)
從代碼可以看出binder1st的實作,實制上是把參數儲存起來,等到調用時,再取出來使用。
boost::bind從原理上來說,也是這麼回事,但是要複雜得多。
解析簡化版bind2
下面是從boost::bind源碼中摳出來的,一個簡單的bind的實作(bind改為bind2),主流編譯器應該都可以編統執行。
為了友善了解程式運作過程,代碼中增加了一些cout輸出。
myBind.h:
#ifndef BOOST_BIND_BIND_HPP_INCLUDED__Mybind
#define BOOST_BIND_BIND_HPP_INCLUDED__Mybind
#include <iostream>
using namespace std;
namespace boost {
template<class T> struct is_placeholder {
enum _vt {
value = 0
};
};
template<int I> struct arg {
arg() {
}
};
template<class T>
struct type {
};
namespace _bi // implementation details
{
template<class A1> struct storage1 {
explicit storage1(A1 a1) :
a1_(a1) {
cout<<"storage1 storage1(A1 a1)"<<endl;
}
A1 a1_;
};
template<int I> struct storage1<boost::arg<I> > {
explicit storage1(boost::arg<I>) {
cout<<"storage1 storage1(boost::arg<I>)"<<endl;
}
static boost::arg<I> a1_() {
return boost::arg<I>();
}
};
template<int I> struct storage1<boost::arg<I> (*)()> {
explicit storage1(boost::arg<I> (*)()) {
cout<<"storage1 storage1(boost::arg<I> (*)())"<<endl;
}
static boost::arg<I> a1_() {
return boost::arg<I>();
}
};
// 2
template<class A1, class A2> struct storage2: public storage1<A1> {
typedef storage1<A1> inherited;
storage2(A1 a1, A2 a2) :
storage1<A1>(a1), a2_(a2) {
cout<<"storage2 storage2(A1 a1, A2 a2)"<<endl;
}
A2 a2_;
};
template<class A1, int I> struct storage2<A1, boost::arg<I> > : public storage1<
A1> {
typedef storage1<A1> inherited;
storage2(A1 a1, boost::arg<I>) :
storage1<A1>(a1) {
cout<<"storage2 storage2(A1 a1, boost::arg<I>)"<<endl;
}
static boost::arg<I> a2_() {
return boost::arg<I>();
}
};
template<class A1, int I> struct storage2<A1, boost::arg<I> (*)()> : public storage1<
A1> {
typedef storage1<A1> inherited;
storage2(A1 a1, boost::arg<I> (*)()) :
storage1<A1>(a1) {
cout<<"storage2 storage2(A1 a1, boost::arg<I> (*)())"<<endl;
}
static boost::arg<I> a2_() {
return boost::arg<I>();
}
};
// result_traits
template<class R, class F> struct result_traits {
typedef R type;
};
struct unspecified {
};
template<class F> struct result_traits<unspecified, F> {
typedef typename F::result_type type;
};
// value
template<class T> class value {
public:
value(T const & t) :
t_(t) {
}
T & get() {
return t_;
}
private:
T t_;
};
// type
template<class T> class type {
};
// unwrap
template<class F> struct unwrapper {
static inline F & unwrap(F & f, long) {
return f;
}
};
// listN
class list0 {
public:
list0() {
}
template<class T> T & operator[](_bi::value<T> & v) const {
cout << "list0 T & operator[](_bi::value<T> & v)" << endl;
return v.get();
}
template<class R, class F, class A> R operator()(type<R>, F & f, A &,
long) {
cout << "list0 R operator()(type<R>, F & f, A &, long)" << endl;
return unwrapper<F>::unwrap(f, 0)();
}
template<class F, class A> void operator()(type<void>, F & f, A &, int) {
cout << "list0 void operator()(type<void>, F & f, A &, int)" << endl;
unwrapper<F>::unwrap(f, 0)();
}
};
template<class A1> class list1: private storage1<A1> {
private:
typedef storage1<A1> base_type;
public:
explicit list1(A1 a1) :
base_type(a1) {
}
A1 operator[](boost::arg<1>) const {
return base_type::a1_;
}
A1 operator[](boost::arg<1> (*)()) const {
return base_type::a1_;
}
template<class T> T & operator[](_bi::value<T> & v) const {
return v.get();
}
template<class R, class F, class A> R operator()(type<R>, F & f, A & a,
long) {
return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]);
}
// template<class F, class A> void operator()(type<void>, F & f, A & a, int) {
// unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]);
// }
};
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) {
}
A1 operator[](boost::arg<1>) const {
cout << "list2 A1 operator[](boost::arg<1>)" << endl;
return base_type::a1_;
}
A2 operator[](boost::arg<2>) const {
cout << "list2 A1 operator[](boost::arg<2>)" << endl;
return base_type::a2_;
}
A1 operator[](boost::arg<1> (*)()) const {
cout << "list2 A1 operator[](boost::arg<1> (*)())" << endl;
return base_type::a1_;
}
A2 operator[](boost::arg<2> (*)()) const {
cout << "list2 A1 operator[](boost::arg<2> (*)())" << endl;
return base_type::a2_;
}
template<class T> T & operator[](_bi::value<T> & v) const {
cout << "T & operator[](_bi::value<T> & v)" << endl;
return v.get();
}
template<class R, class F, class A> R operator()(type<R>, F & f, A & a,
long) {
return f(a[base_type::a1_], a[base_type::a2_]);
// return 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_]);
// }
};
// bind_t
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) {
}
typedef typename result_traits<R, F>::type result_type;
result_type operator()() {
cout << "bind_t::result_type operator()()" << endl;
list0 a;
return l_(type<result_type>(), f_, a, 0);
}
template<class A1> result_type operator()(A1 & a1) {
list1<A1 &> a(a1);
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);
return l_(type<result_type>(), f_, a, 0);
}
private:
F f_;
L l_;
};
template<class T, int I> struct add_value_2 {
typedef boost::arg<I> type;
};
template<class T> struct add_value_2<T, 0> {
typedef _bi::value<T> type;
};
template<class T> struct add_value {
typedef typename add_value_2<T, boost::is_placeholder<T>::value>::type type;
};
template<class T> struct add_value<value<T> > {
typedef _bi::value<T> type;
};
template<int I> struct add_value<arg<I> > {
typedef boost::arg<I> type;
};
//template<int I> struct add_value<arg<I> (*)()> {
// typedef boost::arg<I> (*type)();
//};
template<class R, class F, class L> struct add_value<bind_t<R, F, L> > {
typedef bind_t<R, F, L> type;
};
// list_av_N
template<class A1> struct list_av_1 {
typedef typename add_value<A1>::type B1;
typedef list1<B1> 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;
};
} // namespace _bi
// function pointers
template<class R>
_bi::bind_t<R, R (*)(), _bi::list0> bind2(R (*f)()) {
typedef R (*F)();
typedef _bi::list0 list_type;
return _bi::bind_t<R, F, list_type>(f, list_type());
}
template<class R, class B1, class A1>
_bi::bind_t<R, R (*)(B1), typename _bi::list_av_1<A1>::type> bind2(R (*f)(B1),
A1 a1) {
typedef R (*F)(B1);
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 B1, class B2, class A1, class A2>
_bi::bind_t<R, R (*)(B1, B2), typename _bi::list_av_2<A1, A2>::type> bind2(
R (*f)(B1, B2), A1 a1, A2 a2) {
typedef R (*F)(B1, B2);
typedef typename _bi::list_av_2<A1, A2>::type list_type;
return _bi::bind_t<R, F, list_type>(f, list_type(a1, a2));
}
} // namespace boost
namespace {
boost::arg<1> _1;
boost::arg<2> _2;
}
#endif // #ifndef BOOST_BIND_BIND_HPP_INCLUDED__Mybind
main.cpp:
#include <iostream>
#include "myBind.h"
using namespace std;
void tow_arguments(int i1, int i2) {
std::cout << i1 << i2 << '\n';
}
class Test{
};
void testClass(Test t, int i){
cout<<"testClass,Test,int"<<endl;
}
int main() {
int i1 = 1, i2 = 2;
(boost::bind2(&tow_arguments, 123, _1))(i1, i2);
(boost::bind2(&tow_arguments, _1, _2))(i1, i2);
(boost::bind2(&tow_arguments, _2, _1))(i1, i2);
(boost::bind2(&tow_arguments, _1, _1))(i1, i2);
(boost::bind2(&tow_arguments, 222, 666))(i1, i2);
Test t;
(boost::bind2(&testClass, _1, 666))(t, i2);
(boost::bind2(&testClass, _1, 666))(t);
(boost::bind2(&testClass, t, 666))();
}
在上面的代碼中有很多個類,有幾個是比較重要的any,storage,list和bind_t。
先來看類any:
template<int I> struct arg {
arg() {
}
};
namespace {
boost::arg<1> _1;
boost::arg<2> _2;
}
在上面的代碼中,我們看到了_1和_2,沒錯,所謂的占位符就是這麼個東東,在匿名名字空間中定義的空的struct。
接下來是storage1,storage2類:
template<class A1> struct storage1 {
explicit storage1(A1 a1) :
a1_(a1) {
}
A1 a1_;
};
...
// 2
template<class A1, class A2> struct storage2: public storage1<A1> {
typedef storage1<A1> inherited;
storage2(A1 a1, A2 a2) :
storage1<A1>(a1), a2_(a2) {
}
A2 a2_;
};
...
可以看出storage類隻是簡單的繼承關系,實際上storage類是用來存儲bind函數所傳遞進來的參數的值的,之是以采用繼承而不用組合,其實有精妙的用處。
接着看類list1和list2,它倆分别是storage1和storage2的子類(即參數是存放在listN類中的):
template<class A1> class list1: private storage1<A1> {
...
}
template<class A1, class A2> class list2: private storage2<A1, A2> {
...
}
仔細看代碼,可以看到list1和list2重載了operator [ ] 和operator () 函數,這些重載函數很關鍵,下面會談到。
再看類bind_t:
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) {
}
typedef typename result_traits<R, F>::type result_type;
result_type operator()() {
cout<<"bind_t::result_type operator()()"<<endl;
list0 a;
return l_(type<result_type>(), f_, a, 0);
}
...
private:
F f_; //bind所綁定的函數
L l_; //實際是是listN類,存放bind傳綁定的參數
};
同樣,我們可以看到bind_t類重載了operator ()函數,實際上,bind_t類是bind函數的傳回類型,bind_t實際上是一個stl意義上的funtor。
注意bind_t的兩個成員F f_ 和 L l_,這裡是關鍵之處。
介紹完關鍵類,下面以main.cpp中下面部分代碼為例,詳細說明它的工作流程。
int i1 = 1, i2 = 2;
(boost::bind2(&tow_arguments, 123, _1))(i1, i2);
首先bind2函數傳回一個bind_t類,這個類中的F成員,儲存了tow_arguments函數指針,L成員(即list2類),儲存了參數123 和 _1。
bind_t類采用的是以下的特化:
template<class R, class B1, class B2, class A1, class A2>
_bi::bind_t<R, R (*)(B1, B2), typename _bi::list_av_2<A1, A2>::type> bind2(
R (*f)(B1, B2), A1 a1, A2 a2)
其中storage2類(list2的父類)和storage1類,分别采用的是以下的特化:
template<class A1, int I> struct storage2<A1, boost::arg<I> > : public storage1<A1>
template<class A1> struct storage1
(這裡可以試下把123和_1互換位置,就能發現storage系列類用繼承的精妙之處了,這裡不展開了)
當bind_t調用operator (i1, i2)函數時,即以下函數:
template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2) {
list2<A1 &, A2 &> a(a1, a2);
return l_(type<result_type>(), f_, a, 0); //operator ()
}
再次生成一個list2,這個list2中儲存的是i1 和 i2的值。接着調用l_.operator() (type<result_type>(), f_, a, 0)函數(還記得l_是什麼東東不?)
l_實際是上剛才儲存了123和_1的list2!而f_是由bind函資料綁定的函數的指針!
接着看list2的operator() 函數的實作:
template<class R, class F, class A> R operator()(type<R>, F & f, A & a,
long) {
return f(a[base_type::a1_], a[base_type::a2_]);
// return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); //本是這句的,簡化了下,無關要緊
}
可以看到f是bind所綁定的函數的指針,即這裡是調用了我們所綁定的函數。那麼參數是從哪裡來的?
注意A &a實際上是儲存了i1和i2的list2!,是以這裡又調用了list2的operator[ ]函數!
再看下list2的operator[] 函數的實作:
A1 operator[](boost::arg<1>) const {
cout << "list2 A1 operator[](boost::arg<1>)" << endl;
return base_type::a1_;
}
A2 operator[](boost::arg<2>) const {
cout << "list2 A1 operator[](boost::arg<2>)" << endl;
return base_type::a2_;
}
A1 operator[](boost::arg<1> (*)()) const {
cout << "list2 A1 operator[](boost::arg<1> (*)())" << endl;
return base_type::a1_;
}
A2 operator[](boost::arg<2> (*)()) const {
cout << "list2 A1 operator[](boost::arg<2> (*)())" << endl;
return base_type::a2_;
}
template<class T> T & operator[](_bi::value<T> & v) const {
cout << "T & operator[](_bi::value<T> & v)" << endl;
return v.get();
}
貌似問題都解決了,其實這裡還有一個關鍵要素,兩個list2是怎麼合并起來,組成正确的參數傳遞給函數f的?(第一個list2存放了123和_1,第二個list2存放了i1和i2)
傳遞給tow_arguments函數的參數實際是是(123, 1)!
這裡要仔細研究這些operator[]函數的實作,才能真正了解其過程。
注意,上面的的代碼中bind2隻能綁定普通的函數,不能綁定類成員函數,functor,智能指針等。不過其實差別不大,隻是增加一些特化的代碼而已。
還有一些const相關的函數删掉了。
後記:
從代碼可以看到boost::bind和原生的函數指針相比,會損失效率(兩次的尋址,函數參數的拷貝,有一些可能沒有inline的函數調用)。
不得不吐槽下boost的代碼中那些神奇的宏,如果不是IDE有提示,我想真心弄不明白到底哪段是有意義的。。有時候還很神奇地include一部分代碼進來(不是頭檔案,隻是實作的一部分!)。
不得不吐槽下模闆程式設計中的const,為了一個函數,比如int sum(int a, int b); 就得寫四個重載函數來對應不同參數是或者不是const的情況。是以大家可以想像bind最多支援9個參數,那麼有多少種情況了。
boost::bind的實作的确非常精巧,隐藏了很多複雜性。在《C++沉思錄》中作者說到世界是複雜的,可以通過複雜性擷取簡單性。也許這個是對的,但是對于無數的後來的程式員總會有人想要看看黑盒子裡到底是什麼東東,結果總要花大量的時間才能了解,才能獲得這種“簡單性”。
C++的模闆代碼中最痛苦的是到處都是typedef,一個typedef就把所有的類型資訊都幹掉了!而這東東又是必須的。
也許C++給程式設計語言技術帶來的最大貢獻就是牛B的編譯器了!
C++11中貌似部分的支援concept,希望這個能簡化模闆程式設計。