boost::function的簡單實現

前言

boost::function和boost:bind是一對強大的利器。相信用過的童鞋多少有些體會。html

雖然平時在用boost::function,可是用的時候心中總會一些不安,由於不知道它是怎麼實現的。因而,就本身琢磨着簡單的實現一下,搞明白基本的原理。函數

對於這個簡單實現,有如下幾個目標:測試

  • 選取比較常見的接收2個參數的狀況。
  • 支持普通函數/函數指針、成員函數指針。
  • 兼容函數對象、函數適配器/boost::bind。

實現

首先,定義一個基類:spa

template<typename R, typename T1, typename T2>
class base
{
public:
    virtual ~base()
    {
    }

    virtual R operator()(T1, T2) = 0;
};

而後再實現一個普通函數/函數指針的版本:指針

template<typename R, typename T1, typename T2>
class func : public base<R, T1, T2>
{
public:
    func(R (*ptr)(T1, T2))
        : ptr_(ptr)
    {
    }

    virtual R operator()(T1 a, T2 b)
    {
        return ptr_(a, b);
    }

private:
    R (*ptr_)(T1, T2);
};

接着,實現支持成員函數指針的版本:htm

template<typename R, typename Class, typename T>
class member : public base<R, Class, T>
{
};

template<typename R, typename Class, typename T>
class member<R, Class*, T> : public base<R, Class*, T>
{
public:
    member(R (Class::*ptr)(T))
        :    ptr_(ptr)
    {
    }

    virtual R operator()(Class* obj, T a)
    {
        return (obj->*ptr_)(a);
    }

private:
    R (Class::*ptr_)(T);
};

可能有的童鞋要問,爲何這裏要有一個空的member類呢?這個問題放到下面解釋。對象

天然的輪到最後一個種狀況,函數對象/boost::bind類型的了:blog

template<typename T, typename R, typename T1, typename T2>
class functor : public base<R, T1, T2>
{
public:
    functor(const T& obj)
        : obj_(obj)
    {
    }

    virtual R operator()(T1 a, T2 b)
    {
        return obj_(a, b);
    }

private:
    T obj_;
};

最後,就是可用的function類了,實現以下:get

template<typename T>
class function
{
};

template<typename R, typename T1, typename T2>
class function<R (T1, T2)>
{
public:
    template<typename Class, typename _R, typename _T2>
    function(_R (Class::*ptr)(_T2))
        : ptr_(new member<R, T1, T2>(ptr))
    {
    }

    template<typename _R, typename _T1, typename _T2>
    function(_R (*ptr)(_T1, _T2))
        : ptr_(new func<R, T1, T2>(ptr))
    {
    }

    template<typename T>
    function(const T& obj)
        : ptr_(new functor<T, R, T1, T2>(obj))
    {
    }

    ~function()
    {
        delete ptr_;
    }

    virtual R operator()(T1 a, T2 b)
    {
        return ptr_->operator()(a, b);
    }

private:
    base<R, T1, T2>* ptr_;
};

你們可能注意到了,和前面的member類同樣,function也有一個空的類,那麼這些有什麼用呢?編譯器

這麼作的緣由,主要是利用模板偏特化來進行類型萃取,正常的function聲明的時候,好比function<int (int, int)>而不是func<int, int, int>。因此用模板的偏特化的版本

template<typename R, typename T1, typename T2>
class function<R (T1, T2)>

就能夠把int (int, int)萃取爲R = int,T1 = int,T2 = int了。

同理,對於member類,因爲通常咱們將成員函數指針綁定到function的時候,好比int function(Type*, int),其中Type是成員函數所屬類。也就是說在function中的成員ptr_的類型是base<int, Type*, int>,那麼在function的構造函數中構造的member類的類型就是member<int, Type*, int>,也就是Class = Type*,可是咱們須要的倒是Class = Type。因此這裏得用偏特化萃取一下:

template<typename R, typename Class, typename T>
class member<R, Class*, T> : public base<R, Class*, T>

這樣獲得的Class模板形參就會被編譯器決議爲Type,而不是Type*了。

另外提一下,在function的3種狀況的構造函數是模板成員函數,而不是普通成員函數:

    template<typename Class, typename _R, typename _T2>
    function(_R (Class::*ptr)(_T2))
        : ptr_(new member<R, T1, T2>(ptr))
    {
    }

    template<typename _R, typename _T1, typename _T2>
    function(_R (*ptr)(_T1, _T2))
        : ptr_(new func<R, T1, T2>(ptr))
    {
    }

    template<typename T>
    function(const T& obj)
        : ptr_(new functor<T, R, T1, T2>(obj))
    {
    }

前2種狀況,普通函數/函數指針對應的構造函數和成員函數指針對應的構造函數實現爲成員模板,主要是爲了兼容參數的隱式轉換,例如聲明一個function的類型爲function<int (int, int)> foo,調用的時候卻傳入兩個double類型,foo(1.1, 2.2), double類型隱式轉換成了int類型。這樣也符合boost:function原本的兼容可轉換的調用物這一特性。

而第3種狀況的成員模板,是爲了獲取傳入的函數對象/boost::bind的類型,以便在存儲在functor的數據成員中,這也是爲何functor類的模板參數比其餘版本多了一個的緣由。

而後,咱們來測試一下: 

int get(int a, int b)
{
    std::cout << a+b << std::endl;
    return 0;
}

class Point
{
public:
    int get(int a)
    {
        std::cout << "Point::get called: a = "<< a << std::endl;
        return a;
    }
    int doit(int a, int b)
    {
        std::cout << "Point::doit called: a = "<< a+b << std::endl;
        return a+b;
    }
};

int main(int argc, char const *argv[])
{
    function<int (int, int)> foo(get);
    foo(10.1, 10.3);

    function<int (Point*, int)> bar(&Point::get);
    Point point;
    bar(&point, 30);

    function<int (int, int)> obj(boost::bind(&Point::doit, &point, _1, _2));
    obj(90, 100);
}

結果爲:

20
Point::get called: a = 30 Point::doit called: a = 190

能夠看到,輸出的內容正是所指望的結果。

參考文獻

  1. boost中文手冊. Improved Function Object Adapters 改良的函數對象適配器

(完)

相關文章
相關標籤/搜索