2011-01-31 41 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);