tr1::function与bind的实现原理——深入理解函数配接器

前言:

最近读完了STL的源码,感受最深的不是其中容器类实现,而是其迭代器,配接器以及STL的算法类实现。它将一切的函数,对象,以及类型全部使用typedef等等方法,融合在了一起。这本书之后一定会看第二遍,并且那个时候再来写新的博客吧。

在我使用C++11编写HTTP服务器的时候,使用了std::function,以及std::bind对象和函数,可以将类的成员函数或者静态成员函数绑定给function对象,其中对于各种函数的绑定,以及类对象的绑定,我知道使用了配接器等方法,但是对于具体实现一无所知,因此对此特别的好奇。十分想深入理解一下。因此找到了一篇非常清楚的介绍了原理的文章。

我的github:

寒假刷了150题的leetcode,现在也放在了我的github里,不仅仅有每一题的解题代码,还记录了我解题的时候的思路,以及遇上的问题。是一个很好的总结,欢迎大家前去参观。

https://github.com/YinWenAtBIT

一、全局函数与类成员方法:

1.1全局函数:

function类在功能上是实现了C#的委托,所谓委托,就是一种数据结构,它引用静态方法或者引用类实例的实例方法。通俗地说,委托是指向方法的引用(或指针)。

我们知道CC++可以很容易的将一个全局函数绑定到一个变量上,如:

int Add (int a, int b) { return a+b; }
typedef int(*pAddFunc)(int,int);
int main ()

在上面的例子里,我们定义了一个函数指针类型pAddFunc,它可以绑定一个int(int,int)类型的函数,然后在使用的时候我们可以通过该类型的指针引用函数。

1.2类成员方法:

然而如何能将一个类成员函数绑定到一个函数指针上呢?而我们知道类的成员函数在调用的时候必须引用某个实例,如:

class Add
{
public:
    int add(int a, int b) { return a+b; }
};
int main()
{
    typedef int(Add::*pMemFunc)(int,int);
    pMemFunc add = &Add::add;
    Add a;
    cout<<(a.*add)(3,4)<

也就是说在实现上类成员函数指针比普通函数指针多了一个类实例的引用(或者指针)。那么我们有什么办法可以将类成员函数指针和普通函数指针实现在一个类里面呢?需要提醒一下了,这里有一个陷阱,如果你想只需要一个类就可以完成上述工作,那你就掉进去了。我当初就是这么想的,然后怎么也得不到答案。但是昨天突然间开了窍,原来还需要一点点设计原则——里氏代换原则在里面。

二、全局函数配接器:


为了说明白问题的来龙去脉,我会一步一步的解释。首先我们写一个普通函数的委托类,如:

template
class FunctionOfPtr
{
    typedef _ReturnType(*PtrFuncType)(_T1, _T2);
public:   
    PtrFuncType m_pfunc;
    FunctionOfPtr(PtrFuncType pf)
    {
        m_pfunc = pf;
    }
    virtual PtrFuncType GetFunc()
    {
        return m_pfunc;
    }
    virtual _ReturnType operator()(_T1 arg1, _T2 arg2)
    {
        return (*m_pfunc)(arg1, arg2);
    }   
};
template
FunctionOfPtr<_ReturnType, _T1, _T2> bind(_ReturnType(*pFunc)(_T1, _T2))
{
    return FunctionOfPtr<_ReturnType, _T1, _T2>(pFunc);
}
这样我们就可以很方便的将普通函数委托给 FunctionOfPtr 对象了,为了方便还实现了一个简单的 bind 函数。这样我们在使用普通函数的时候就可以这样用了,如:
int add(int a, int b){ return a+b; }
int main()
{
 
    FunctionOfPtr f = bind(&add);   
    cout<

三、类成员方法配接器:

接下来我们需要实现一个类的成员函数的委托类,如:
template
class FunctionOfMem
{
    typedef _ReturnType(*PtrFuncType)(_T1, _T2);
    typedef _ReturnType(_Class::*MemFuncType)(_T1, _T2);
public:
    MemFuncType m_pfunc;
    _Class* m_obj;
    FunctionOfMem(MemFuncType func, _Class* obj)
    {
        m_pfunc = func;
        m_obj = obj;
    }
    virtual PtrFuncType GetFunc()
    {
        return (PtrFuncType)(*(long*)&m_pfunc);
    }
    virtual _ReturnType operator()(_T1 arg1, _T2 arg2)
    {
        return (m_obj->*m_pfunc)(arg1, arg2);
    }   
};
template
FunctionOfMem<_ReturnType, _Class, _T1, _T2> bind(_ReturnType(_Class::*pMemFunc)(_T1, _T2), _Class* obj)
{
    return FunctionOfMem<_ReturnType, _Class, _T1, _T2>(pMemFunc, obj);
}
这样我们就可以很方便的将类的成员函数委托给 FunctionOfMem 对象了,为了方便也实现了一个简单的 bind 函数。这样我们在使用类成员函数的时候局可以这样用了,如:
class Add
{
public:
    int add(int a, int b) { return a+b; }
};
int main()
{
    Add a;
    FunctionOfMem f = bind(&Add::add, &a);   
    cout<

样我们就分别完成了普通函数和类的成员函数的委托。可是这样的话,普通函数和类的成员函数在定义的时候就不统一了。而STL里面的function是可以实现的。

四、基类virtual方法实现统一:


诶!你再仔细瞅瞅FunctionOfPtrFunctionOfMem的定义,发现了什么?

template
class FunctionOfPtr;

template
class FunctionOfMem;
是的,我发现了。它们在定义的时候只差一个 typename  _Class ,而且它们的内部函数也有很多相同的地方!那么我可能需要一个基类来代替这两个子类了。说干就干,如:

template
class FunctionBase
{
public:
    typedef _ReturnType(*PtrFuncType)(_T1, _T2);
    virtual PtrFuncType GetFunc() = 0;
    virtual _ReturnType operator()(_T1 arg1, _T2 arg2) = 0;
};
 
template
class FunctionOfMem : public FunctionBase<_ReturnType, _T1, _T2>
{
public:   
    typedef _ReturnType(_Class::*MemFuncType)(_T1, _T2);
 
    MemFuncType m_pfunc;
    _Class* m_obj;
 
    FunctionOfMem(MemFuncType func, _Class* obj)
    {
        m_pfunc = func;
        m_obj = obj;
    }
 
    virtual PtrFuncType GetFunc()
    {
        return (PtrFuncType)(*(long*)&m_pfunc);
    }
 
    virtual _ReturnType operator()(_T1 arg1, _T2 arg2)
    {
        return (m_obj->*m_pfunc)(arg1, arg2);
    }   
};
 
template
class FunctionOfPtr : public FunctionBase<_ReturnType, _T1, _T2>
{
public:   
    PtrFuncType m_pfunc;
 
    FunctionOfPtr(PtrFuncType pf)
    {
        m_pfunc = pf;
    }
 
    virtual PtrFuncType GetFunc()
    {
        return m_pfunc;
    }
 
    virtual _ReturnType operator()(_T1 arg1, _T2 arg2)
    {
        return (*m_pfunc)(arg1, arg2);
    }   
};
 
template
class Functor
{
public:
    FunctionBase<_ReturnType, _T1, _T2>* m_pfunc;
 
    Functor() : m_pfunc(NULL)
    {}
    ~Functor()
    {
        if (m_pfunc != NULL)
        {
            delete m_pfunc;
            m_pfunc = NULL;
        }
    }
 
    template
    Functor(_ReturnType(_Class::*pMemFunc)(_T1, _T2), _Class* obj)
    {
        m_pfunc = new FunctionOfMem<_ReturnType, _Class, _T1, _T2>(pMemFunc, obj);
    }
 
    Functor(_ReturnType(*pPtrFunc)(_T1, _T2))
    {
        m_pfunc = new FunctionOfPtr<_ReturnType, _T1, _T2>(pPtrFunc);
    }
 
    _ReturnType operator()(_T1 arg1, _T2 arg2)
    {
        return (*m_pfunc)(arg1, arg2);
    }
 
    Functor& operator=(Functor& f)
    {
        m_pfunc = f.m_pfunc;
        f.m_pfunc = NULL;
        return *this;
    }
};
 
template
Functor<_ReturnType, _T1, _T2> bind(_ReturnType(_Class::*pMemFunc)(_T1, _T2), _Class* obj)
{
    return Functor<_ReturnType, _T1, _T2>(pMemFunc, obj);
}
 
template
Functor<_ReturnType, _T1, _T2> bind(_ReturnType(*pFunc)(_T1, _T2))
{
    return Functor<_ReturnType, _T1, _T2>(pFunc);
}
 
class Add
{
public:
    int add(int a, int b)
    {
        return a+b;
    }
};
 
int add(int a, int b)
{
    return a+b;
}
 
int main()
{
    Add a;
    Functor f;
 
    f = bind(&Add::add, &a);   
    cout<

重新设计后的FunctionOfMemFunctionOfPtr都可以委托给Functor类,这样客户端在使用Functor对象的时候就可以不需要知道到底是调用的普通函数还是类成员函数了。

但是我们的Functor虽然在设计上没有问题了,但是和STL库里面的function还是有点不一样,为什么呢?因为STL库里面的function通过宏定义实现了不同参数(0…10)形式的function。我这里还有一个是用宏定义之后的版本,但是读过之后就会发现,那不是给人读的。

#define _COMMA0     ,
#define _MCOMMA     ,
 
#define YNAME(x, y)    x##y
 
#define FIRST(x)    YNAME(x, 0)
#define _CDR(x)     YNAME(x, 1)
#define LIST(x)     FIRST(x) _MCOMMA _CDR(x)
 
#define FIRST2(x, y)    YNAME(x, 0) YNAME(y, 0)
#define _CDR2(x, y)     YNAME(x, 1) YNAME(y, 1)
#define LIST2(x, y)     FIRST2(x, y) _MCOMMA _CDR2(x, y)
 
#define _CLASS_ARG0     LIST(typename Arg)
#define _CLASS_NO_ARG0     LIST(Arg)
#define _CLASS_WITH_ARG0     LIST2(Arg, arg)
 
#define _C_CLASS_ARG0   _COMMA0 _CLASS_ARG0     // typename Arg0, typename Arg1...
#define _C_CLASS_NO_ARG0   _CLASS_NO_ARG0       // Arg0, Arg1...
#define _C_CLASS_WITH_ARG0   _CLASS_WITH_ARG0   // Arg0 arg0, Arg1 arg1...
 
template
class FunctionBase
{
public:
    typedef _ReturnType(*PtrFuncType)(_C_CLASS_NO_ARG0);
    virtual PtrFuncType GetFunc() = 0;
    virtual _ReturnType operator()(_C_CLASS_WITH_ARG0) = 0;
};
 
template
class FunctionOfMem : public FunctionBase<_ReturnType, _C_CLASS_NO_ARG0>
{
public:   
    typedef _ReturnType(_Class::*MemFuncType)(_C_CLASS_NO_ARG0);
 
    MemFuncType m_pfunc;
    _Class* m_obj;
 
    FunctionOfMem(MemFuncType func, _Class* obj)
    {
        m_pfunc = func;
        m_obj = obj;
    }
 
    virtual PtrFuncType GetFunc()
    {
        return (PtrFuncType)(*(long*)&m_pfunc);
    }
 
    virtual _ReturnType operator()(_C_CLASS_WITH_ARG0)
    {
        return (m_obj->*m_pfunc)(arg0, arg1);
    }   
};
 
template
class FunctionOfPtr : public FunctionBase<_ReturnType, _C_CLASS_NO_ARG0>
{
public:   
    PtrFuncType m_pfunc;
 
    FunctionOfPtr(PtrFuncType pf)
    {
        m_pfunc = pf;
    }
 
    virtual PtrFuncType GetFunc()
    {
        return m_pfunc;
    }
 
    virtual _ReturnType operator()(_C_CLASS_WITH_ARG0)
    {
        return (*m_pfunc)(arg0, arg1);
    }   
};
 
template
class Functor
{
public:
    FunctionBase<_ReturnType, _C_CLASS_NO_ARG0>* m_pfunc;
 
    Functor() : m_pfunc(NULL)
    {}
    ~Functor()
    {
        if (m_pfunc != NULL)
        {
            delete m_pfunc;
            m_pfunc = NULL;
        }
    }
 
    template
    Functor(_ReturnType(_Class::*pMemFunc)(_C_CLASS_NO_ARG0), _Class* obj)
    {
        m_pfunc = new FunctionOfMem<_ReturnType, _Class, _C_CLASS_NO_ARG0>(pMemFunc, obj);
    }
 
    Functor(_ReturnType(*pPtrFunc)(_C_CLASS_NO_ARG0))
    {
        m_pfunc = new FunctionOfPtr<_ReturnType, _C_CLASS_NO_ARG0>(pPtrFunc);
    }
 
    _ReturnType operator()(_C_CLASS_WITH_ARG0)
    {
        return (*m_pfunc)(arg0, arg1);
    }
 
    Functor& operator=(Functor& f)
    {
        m_pfunc = f.m_pfunc;
        f.m_pfunc = NULL;
        return *this;
    }
};
 
template
Functor<_ReturnType, _C_CLASS_NO_ARG0> bind(_ReturnType(_Class::*pMemFunc)(_C_CLASS_NO_ARG0), _Class* obj)
{
    return Functor<_ReturnType, _C_CLASS_NO_ARG0>(pMemFunc, obj);
}
 
template
Functor<_ReturnType, _C_CLASS_NO_ARG0> bind(_ReturnType(*pFunc)(_C_CLASS_NO_ARG0))
{
    return Functor<_ReturnType, _C_CLASS_NO_ARG0>(pFunc);
}
 
class Add
{
public:
    int add(int a, int b)
    {
        return a+b;
    }
};
 
int add(int a, int b)
{
    return a+b;
}
 
int main()
{
    Add a;
    Functor f;
 
    f = bind(&Add::add, &a);   
    cout<

总结:

没想到之前学习过的虚方法,在这里可以这么巧妙的用来实现配接器的统一,真是C++越学月觉得自己好菜,还有许许多多的东西值得我去专研。





你可能感兴趣的:(C++)