将类成员函数绑定到c函数

时间:2011-01-31 19:39:19

标签: c++ templates

下面我有一个将类成员函数绑定到全局函数的概念。这样做的主要目的是使用C ++进行C风格的回调函数实现。这可以用更好的方式完成(例如,没有最终宏或typeof,或使用C ++ 0x功能)?

#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;
}

1 个答案:

答案 0 :(得分: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对象不会隐式转换为函数指针,因为lambda表达式使用lambda-capture。

C ++ 0x,n3225的最新草案声明:

  

没有 lambda-capture lambda-expression 的闭包类型具有公共非虚拟非显式const转换函数,用于指向具有相同参数的函数并返回类型作为闭包类型的函数调用操作符。此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用操作符具有相同的效果。

以下是非法的:

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