C++11 中委派 (Delegates) 的實現

fmms 12年前發布 | 18K 次閱讀 C/C++開發 C/C++

介紹

在 C++ 中通過一個全局函數來綁定到對象的成員函數是很有用的,這個特性也存在于其他語言中,例如 C#的委派。在 C++ 中相當于成員函數指針,但是并沒有提供相應的特性。在這篇文章中,我想提出一個簡單的 C++ 委派的實現,是用 C++ 成員函數指針和 C++11 的可變模板(variadic templates),目前這套實現方法僅支持 GNU C++ 4.7.0,在 Windows 下可使用 MinGW。

背景

在我的方法中獎提供一個 create_delegate 函數,可通過下面兩種方法來調用:

  • create_delegate(&object, &member_function)
  • create_delegate(&function)

第一種方法創建一個對象并提供一個 operator() 成員函數,第二個方法生成一個函數指針,兩種方法都兼容 type function<...>

示例程序

首先我們定義一個包含多個方法的類:

class A
{
    int i;
public:    
    A(int k):i(k) {}

    auto get()const ->int { return i;}    
    auto set(int v)->void { i = v;}

    auto inc(int g)->int& { i+=g; return i;}
    auto incp(int& g)->int& { g+=i; return g;}

    auto f5 (int a1, int a2, int a3, int a4, int a5)const ->int
    {
        return i+a1+a2+a3+a4+a5;
    }

    auto set_sum4(int &k, int a1, int a2, int a3, int a4)->void
    {
        i+=a1+a2+a3+a4;
        k = i;
    }

    auto f8 (int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) const ->int
    {
        return i+a1+a2+a3+a4+a5+a6+a7+a8;
    }    

    static auto sqr(double x)->double { return x*x; }
};

請注意你并不需要一定使用 C++ 的 auto 函數語法,你也可以使用傳統的方法,然后我們使用下面方法創建一個類:

A a(11);

接下來我們創建委派:

auto set1 = create_delegate(&a,&A::set);
auto inc = create_delegate(&a,&A::inc);
std::function<int(int&)> incp = create_delegate(&a,&A::incp);
auto af5  = create_delegate(&a,&A::f5);
auto set_sum4= create_delegate(&a,&A::set_sum4);
auto af8  = create_delegate(&a,&A::f8);
auto sqr = create_delegate(&A::sqr); // static function </int(int&)>

為了演示,我們使用 auto 或者 function<...>. 然后我們開始調用委派:

set1(25);
int x = 5;
int k = inc(x);
k = incp(x);
std::cout << "a.get():" << a.get() << std::endl;
std::cout << "k: " << k << std::endl;
std::cout << "x: " << x << std::endl;
std::cout << "af5(1,2,3,4,5): " << af5(1,2,3,4,5) << std::endl;

set_sum4(x,1,2,3,20);
std::cout << "after set_sum4(x,1,2,3,20)" << std::endl;
std::cout << "a.get(): " << a.get() << std::endl;
std::cout << "x: " << x << std::endl;
std::cout << "af8(1,2,3,4,5,6,7,8): " << af8(1,2,3,4,5,6,7,8) << std::endl;
std::cout << "sqr(2.1): " << sqr(2.1) << std::endl;

執行上述程序的打印結果如下:

a.get():30
k: 35
x: 35
af5(1,2,3,4,5): 45
after set_sum4(x,1,2,3,20)
a.get(): 56
x: 56
af8(1,2,3,4,5,6,7,8): 92
sqr(2.1): 4.41

關鍵點

對于一個不是 volatile 和 const 的簡單函數而言,實現是非常簡單的,我們只需要創建一個類保存兩個指針,一個是對象,另外一個是成員函數:
template <class T, class R, class ... P>
struct  _mem_delegate
{
    T* m_t;
    R  (T::*m_f)(P ...);
    _mem_delegate(T* t, R  (T::*f)(P ...) ):m_t(t),m_f(f) {}
    R operator()(P ... p) 
    {
            return (m_t->*m_f)(p ...);
    }
};  

可變模板 variadic template 允許定義任意個數和類型參數的 operator() 函數,而 create_function 實現只需簡單返回該類的對象:

template <class T, class R, class ... P>
_mem_delegate<T,R,P ...> create_delegate(T* t, R (T::*f)(P ...))
{
    _mem_delegate<T,R,P ...> d(t,f);
    return d;
}

實際中,我們需要另外的三個實現用于覆蓋 const、volatile 和 const volatile 三種成員函數,這也是為什么傳統使用 #define 宏很便捷的原因,讓你無需重寫代碼段,下面是完整的實現:

template <class F>
F* create_delegate(F* f)
{
    return f;
}
#define _MEM_DELEGATES(_Q,_NAME)\
template <class T, class R, class ... P>\
struct _mem_delegate ## _NAME\
{\
    T* m_t;\
    R  (T::*m_f)(P ...) _Q;\
    _mem_delegate ## _NAME(T* t, R  (T::*f)(P ...) _Q):m_t(t),m_f(f) {}\
    R operator()(P ... p) _Q\
    {\
        return (m_t->*m_f)(p ...);\
    }\
};\
\
template <class T, class R, class ... P>\
    _mem_delegate ## _NAME<T,R,P ...> create_delegate(T* t, R (T::*f)(P ...) _Q)\
{\
    _mem_delegate ##_NAME<T,R,P ...> d(t,f);\
    return d;\
}

_MEM_DELEGATES(,Z)
_MEM_DELEGATES(const,X)
_MEM_DELEGATES(volatile,Y)
_MEM_DELEGATES(const volatile,W)

參考資料

  1. http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
  2. http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates
  3. [3] http://www.codeproject.com/Articles/13287/Fast-C-Delegate

英文原文 

 本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!