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)<<endl;
    return 0;
}

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

二、全局函数配接器:


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

template<typename _ReturnType, typename _T1, typename _T2>
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<typename _ReturnType, typename _T1, typename _T2>
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<int,int,int> f = bind(&add);   
    cout<<f(2, 4)<<endl;
    return 0;
}

三、类成员方法配接器:

接下来我们需要实现一个类的成员函数的委托类,如:
template<typename _ReturnType, typename _Class, typename _T1, typename _T2>
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<typename _ReturnType, typename _Class, typename _T1, typename _T2>
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<int,Add,int,int> f = bind(&Add::add, &a);   
    cout<<f(2, 4)<<endl;
    return 0;
}

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

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


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

template<typename _ReturnType, typename _T1, typename _T2>
class FunctionOfPtr;

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

template<typename _ReturnType, typename _T1, typename _T2>
class FunctionBase
{
public:
    typedef _ReturnType(*PtrFuncType)(_T1, _T2);
    virtual PtrFuncType GetFunc() = 0;
    virtual _ReturnType operator()(_T1 arg1, _T2 arg2) = 0;
};
 
template<typename _ReturnType, typename _Class, typename _T1, typename _T2>
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<typename _ReturnType, typename _T1, typename _T2>
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<typename _ReturnType, typename _T1, typename _T2>
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<typename _Class>
    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<typename _ReturnType, typename _Class, typename _T1, typename _T2>
Functor<_ReturnType, _T1, _T2> bind(_ReturnType(_Class::*pMemFunc)(_T1, _T2), _Class* obj)
{
    return Functor<_ReturnType, _T1, _T2>(pMemFunc, obj);
}
 
template<typename _ReturnType, typename _T1, typename _T2>
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<int,int,int> f;
 
    f = bind(&Add::add, &a);   
    cout<<f(2, 4)<<endl;
 
    f = bind(&add);   
    cout<<f(2, 4)<<endl;
 
    return 0;
}

重新设计后的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<typename _ReturnType _C_CLASS_ARG0>
class FunctionBase
{
public:
    typedef _ReturnType(*PtrFuncType)(_C_CLASS_NO_ARG0);
    virtual PtrFuncType GetFunc() = 0;
    virtual _ReturnType operator()(_C_CLASS_WITH_ARG0) = 0;
};
 
template<typename _ReturnType, typename _Class _C_CLASS_ARG0>
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<typename _ReturnType _C_CLASS_ARG0>
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<typename _ReturnType _C_CLASS_ARG0>
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<typename _Class>
    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<typename _ReturnType, typename _Class _C_CLASS_ARG0>
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<typename _ReturnType _C_CLASS_ARG0>
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<int,int,int> f;
 
    f = bind(&Add::add, &a);   
    cout<<f(2, 4)<<endl;
 
    f = bind(&add);   
    cout<<f(2, 4)<<endl;
 
    return 0;
}
 
 

总结:

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





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值