Preface
After C ++ 11, STD: BIND is a component of the C ++ standard library. At the beginning, I tried to get a C ++ 11 Implementation and found that the variable parameter template was used in it (the Code became amazing ).
Http://llvm.org/svn/llvm-project/libcxx/trunk/include/functional
Let's take a look at the boost implementation of the original point.
There are also many articles about boost: bind implementation on the Internet, but most of them are post a piece of code and try again. The results will not be clear at the end. (At least LZ is ..)
It took a day to finally extract the programmable code from the boost: bind source code.
The following is a step-by-step parsing of Boost: bind implementation.
Implementation of fouctor and bind1st in the standard library
Start with a simple one. First, let's take a look at the implementation of fouctor and bind1st in the standard library (to prevent name conflicts with the standard library, 2 is added after all the names ).
#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";}
The above implementation is relatively simple, and I hope I have not looked dizzy. :)
From the code, we can see the implementation of binder1st. In practice, we save the parameters and use them after the call.
Boost: The same is true for the BIND principle, but it is much more complicated.
Brief Analysis of bind2
The following is a simple implementation of BIND from boost: bind source code (BIND is changed to bind2). Mainstream compilers should be able to compile and execute it.
To facilitate understanding of the program running process, some cout output is added to the Code.
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>();}};// 2template<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_traitstemplate<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;};// valuetemplate<class T> class value {public:value(T const & t) :t_(t) {}T & get() {return t_;}private:T t_;};// typetemplate<class T> class type {};// unwraptemplate<class F> struct unwrapper {static inline F & unwrap(F & f, long) {return f;}};// listNclass 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_ttemplate<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_Ntemplate<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 pointerstemplate<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 boostnamespace {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))();}
In the above Code, there are many classes, several of which are important: Any, storage, list, And bind_t.
First, let's look at the class any:
template<int I> struct arg {arg() {}};namespace {boost::arg<1> _1;boost::arg<2> _2;}
In the code above, we can see _ 1 and _ 2. That's right. The so-called placeholder is such an empty struct defined in the anonymous namespace.
The following are storage1 and storage2 classes:
template<class A1> struct storage1 {explicit storage1(A1 a1) :a1_(a1) {}A1 a1_;};...// 2template<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_;};...
It can be seen that the storage class is just a simple inheritance relationship,In fact, the storage class is used to store the values of the parameters passed by the BIND function,The use of inheritance instead of combination is actually of subtle use.
Next, let's look at the list1 and list2 classes,The two are the sub-classes of storage1 and storage2 respectively (that is, the parameters are stored in the listn class):
template<class A1> class list1: private storage1<A1> {...}template<class A1, class A2> class list2: private storage2<A1, A2> {...}
Read the code carefully and you can seeList1 and list2 reload operator [] and operator () FunctionsThese overload functions are critical. We will discuss them below.
Look at the class bind_t:
Template <class R, Class F, class L> class bind_t {public: typedef bind_t this_type; bind_t (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 _;// Bind the bound functionL _;// It is actually the listn class, which stores the bind-passed binding parameters};
Similarly, we can see that the bind_t class reloads the operator () function. In fact, the bind_t class is the return type of the BIND function, and bind_t is actually a funtor In the STL sense.
Note that bind_t has two members: F _ and L _. Here is the key.
After the key classes are introduced, the following code in Main. cpp is used as an example to describe its workflow in detail.
int i1 = 1, i2 = 2;(boost::bind2(&tow_arguments, 123, _1))(i1, i2);
First, the bind2 function returns a bind_t class. The f Member in this class saves the tow_arguments function pointer, the L member (that is, the list2 class), and saves the parameters 123 and _ 1.
The bind_t class adopts the following special features:
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)
The storage2 class (the parent class of list2) and storage1 classes use the following features:
template<class A1, int I> struct storage2<A1, boost::arg<I> > : public storage1<A1>
template<class A1> struct storage1
(Here we can try to swap the 123 and _ 1 locations to find out the subtleties of the storage series class with inheritance. I will not start it here)
When bind_t calls the operator (I1, I2) function, it is the following function:
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 ()}
Generate another list2, which stores I1 and I2 values. Then call the L _. Operator () (type <result_type> (), F _, A, 0) function (remember what l _ is ?)
L _ actually saved the list2 of 123 and _ 1 just now! F _ is the pointer of the function bound to the BIND function!
Next, let's look at the implementation of the operator () function of list2:
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 _]); // This sentence is simplified and irrelevant}
We can see that F is the pointer to the function bound to bind, that is, the bound function is called here. So where does the parameter come from?
Note that A & A actually saves the I1 and I2 list2 !, So here we call the operator [] function of list2!
Let's look at the implementation of the operator [] function of list2:
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();}
It seems that the problem has been solved. In fact, there is another key factor here. How are the two list2 combined to form the correct parameter and pass it to function f? (The first list2 stores 123 and _ 1, and the second list2 stores I1 and I2)
The parameter passed to the tow_arguments function is actually (123, 1 )!
Here we need to carefully study the implementation of these operator [] functions to truly understand the process.
Note: In the code above, bind2 can only bind common functions, but cannot bind class member functions, functor, and smart pointers. But the difference is not big, just adding some special code.
Some const-related functions are deleted.
Postscript:
From the code, we can see that boost: Compared with the native function pointer, bind will lose efficiency (two addressing, copying of function parameters, some function calls that may not have inline ).
I had to vomit the magic Macros in the boost code. If it wasn't a prompt from IDE, I really couldn't figure out which part is meaningful .. Sometimes it is amazing to include a part of the Code (not a header file, but a part of the implementation !).
I have to vomit the const in template programming, for a function, such as int sum (int A, int B ); you have to write four overload functions to correspond to different parameters or not Const. So you can imagine how many cases a bind supports a maximum of nine parameters.
Boost: The implementation of BIND is indeed very delicate, hiding a lot of complexity. In the book "C ++ meditation", the author said that the world is complex and simplicity can be obtained through complexity. Maybe this is correct, but for countless programmers, there will always be people who want to see what is in the black box, and it takes a lot of time to understand the results to get this "Simplicity ".
In the template code of C ++, the most painful thing is that typedef is everywhere. A typedef will kill all the type information! And this stuff is necessary.
Maybe C ++'s greatest contribution to programming language technology is niub 'S Compiler!
In C ++ 11, concept is supported. We hope this can simplify template programming.