2011-01-31 44 views
5

下面我有一個將類成員函數綁定到全局函數的概念。這樣做的主要目的是使用C++進行C風格的回調函數實現。這可以以更好的方式完成(例如,沒有最終宏或typeof,或使用C++ 0x功能)?將類成員函數綁定到c函數

#include <iostream> 

using namespace std; 

template<typename MF> struct mf_wrapper_helper; 

template<typename R, typename T> 
struct mf_wrapper_helper<R (T::*)()> 
{ 
    template <R (T::*F)()> 
    static R wrapper(T *foo) { return (foo->*F)(); } 
}; 

template<typename R, typename T, typename T1> 
struct mf_wrapper_helper<R (T::*)(T1)> 
{ 
    template <R (T::*F)(T1)> 
    static R wrapper(T *foo, T1 arg1) { return (foo->*F)(arg1); } 
}; 

#define BIND_MF(mf) \ 
    mf_wrapper_helper<typeof(mf)>::wrapper<mf> 


struct Foo 
{ 
    void x() { cout << "Foo::x()" << endl; } 
    void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; } 
}; 

int 
main() 
{ 
    typedef void (*f_t)(Foo *); 
    typedef void (*f1_t)(Foo *, int i); 

    Foo foo; 

    f_t f_p = BIND_MF(&Foo::x); 
    (*f_p)(&foo); 

    f1_t f1_p = BIND_MF(&Foo::x1); 
    (*f1_p)(&foo, 314); 

    return 0; 
} 
+0

什麼編譯您使用的?如果您可以使用C++ 0x,只需使用無捕獲的lambda,就可以將它們轉換爲函數指針。另外,爲什麼動態分配`main`中的東西? – GManNickG 2011-01-31 19:48:56

+0

忽略那個`new` - 這個問題無關緊要。我使用GCC 4.5和ICC 11.1。不知道lambda如何幫助這裏,因爲`Lambda函數是實現相關類型的函數對象`。其實,我不太瞭解C++ 0x,代碼示例非常感謝。 – klimkin 2011-01-31 20:26:13

回答

1

我認爲只有這將很好地工作的技術是寫你希望一個C回調中調用每個成員函數C包裝功能;即:

extern "C" void Foo_x(Foo *foo) 
{ 
    foo->x(); 
} 

extern "C" void Foo_x1(Foo *foo, int i) 
{ 
    foo->x1(i); 
} 

你也可以使用的C++ 0x,其隱式轉換到函數指針具有相同的參數和返回類型爲封閉類型的函數調用操作符的lambda表達式。但是請記住,函數類型的語言鏈接是「C++」,而不是「C」。

#include <cstdlib> 
#include <iostream> 

using namespace std; 

struct Foo 
{ 
    void x() { cout << "Foo::x()" << endl; } 
    void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; } 
}; 

int main() 
{ 
    typedef void (*f_t)(Foo*); // default ("C++") language linkage 
    typedef void (*f1_t)(Foo*, int); 

    Foo foo; 

    Foo_x(&foo); 
    Foo_x1(&foo, -10); 

    f_t fn = [] (Foo *foo) { 
     foo->x(); 
    }; 
    fn(&foo); 

    f1_t fn1 = [] (Foo *foo, int i) { 
     foo->x1(i); 
    }; 
    fn1(&foo, 314); 

    return EXIT_SUCCESS; 
} 

注意,C++標準的第5.2.2節,函數調用,規定:

通過其功能類型具有語言聯動裝置是從語言聯動不同的表達調用函數被調用函數的定義的函數類型是未定義的。

所以下面技術上調用未定義的行爲:

extern "C" typedef void (*f_t)(Foo*); 

int main() 
{ 
    Foo foo; 

    f_t fn = [] (Foo *foo) { 
     foo->x(); 
    }; 
    fn(&foo); // `fn` is a pointer to a function that uses "C++" language linkage, 
      // but it is erroneously called through "C" language linkage. 

//... 

編輯:有點實驗後,我來到了與返回調用指定的成員函數lambda表達式下面的模板功能:

template <typename return_t, class base, typename... arg_types> 
std::function<return_t (base*, arg_types...)> make_lambda_to_call_member_function(return_t (base::*mem_fn)(arg_types...)) 
{ 
    return [mem_fn] (base *o, arg_types... args) -> return_t { 
     (o->*mem_fn)(args...); 
    }; 
} 

template <typename return_t, class base, typename... arg_types> 
std::function<return_t (const base*, arg_types...)> make_lambda_to_call_member_function(return_t (base::*cmem_fn)(arg_types...) const) 
{ 
    return [cmem_fn] (const base *o, arg_types... args) -> return_t { 
     (o->*cmem_fn)(args...); 
    }; 
} 

如果Foo定義爲:

struct Foo 
{ 
    void x() { cout << "Foo::x()" << endl; } 
    void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; } 
    void cx1(float f) const { cout << "Foo::cx1(" << f << ")" << endl; } 
}; 

然後你使用模板make_lambda_to_call_member_function這樣的:

auto fn = make_lambda_to_call_member_function(&Foo::x); 
fn(&foo); 

auto fn1 = make_lambda_to_call_member_function(&Foo::x1); 
fn1(&foo, 314); 

auto fn2 = make_lambda_to_call_member_function(&Foo::cx1); 
fn2(&foo, 44.832f); 

但是請注意,返回的拉姆達對象不會隱式轉換函數指針,因爲lambda表達式使用拉姆達捕獲。

的C++ 0x的最新草案,n3225,指出:

拉姆達表達沒有拉姆達捕捉閉合類型有一個公共非虛不明確的常量轉換函數指向具有與閉包類型的函數調用操作符相同的參數和返回類型的函數。該轉換函數返回的值應該是一個函數的地址,該函數在調用時具有與調用閉包類型的函數調用操作符相同的效果。

以下是非法的:

void (*fn5)(Foo*) = make_lambda_to_call_member_function(&Foo::x);