下面我有一个将类成员函数绑定到全局函数的概念.这样做的主要目的是使用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;
}
Run Code Online (Sandbox Code Playgroud)
我认为唯一能完美工作的技术是为您希望在 C 回调中调用的每个成员函数编写一个 C 包装函数;IE:
\n\nextern "C" void Foo_x(Foo *foo)\n{\n foo->x();\n}\n\nextern "C" void Foo_x1(Foo *foo, int i)\n{\n foo->x1(i);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n您还可以使用 C++0x 的 lambda 表达式,它隐式转换为指向与闭包类型\xe2\x80\x99s 函数调用运算符具有相同参数和返回类型的函数的指针。但是,请记住,函数类型的语言链接是“C++”,而不是“C”。
\n\n#include <cstdlib>\n#include <iostream>\n\nusing namespace std;\n\nstruct Foo\n{\n void x() { cout << "Foo::x()" << endl; }\n void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; }\n};\n\nint main()\n{\n typedef void (*f_t)(Foo*); // default ("C++") language linkage\n typedef void (*f1_t)(Foo*, int);\n\n Foo foo;\n\n Foo_x(&foo);\n Foo_x1(&foo, -10);\n\n f_t fn = [] (Foo *foo) {\n foo->x();\n };\n fn(&foo);\n\n f1_t fn1 = [] (Foo *foo, int i) {\n foo->x1(i);\n };\n fn1(&foo, 314);\n\n return EXIT_SUCCESS;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n请注意,C++ 标准的第 5.2.2 节“函数调用”指出:
\n\n\n\n\n通过表达式调用函数时,如果其函数类型的语言链接与被调用函数\xe2\x80\x99s 定义的函数类型的语言链接不同,则该函数是未定义的。
\n
因此,从技术上讲,以下内容会调用未定义的行为:
\n\nextern "C" typedef void (*f_t)(Foo*);\n\nint main()\n{\n Foo foo;\n\n f_t fn = [] (Foo *foo) {\n foo->x();\n };\n fn(&foo); // `fn` is a pointer to a function that uses "C++" language linkage,\n // but it is erroneously called through "C" language linkage.\n\n//...\n
Run Code Online (Sandbox Code Playgroud)\n\n编辑:经过一些实验,我想出了以下模板函数,它们返回调用给定成员函数的 lambda:
\n\ntemplate <typename return_t, class base, typename... arg_types>\nstd::function<return_t (base*, arg_types...)> make_lambda_to_call_member_function(return_t (base::*mem_fn)(arg_types...)) \n{\n return [mem_fn] (base *o, arg_types... args) -> return_t {\n (o->*mem_fn)(args...);\n };\n}\n\ntemplate <typename return_t, class base, typename... arg_types>\nstd::function<return_t (const base*, arg_types...)> make_lambda_to_call_member_function(return_t (base::*cmem_fn)(arg_types...) const) \n{\n return [cmem_fn] (const base *o, arg_types... args) -> return_t {\n (o->*cmem_fn)(args...);\n };\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n如果Foo
定义为:
struct Foo\n{\n void x() { cout << "Foo::x()" << endl; }\n void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; }\n void cx1(float f) const { cout << "Foo::cx1(" << f << ")" << endl; }\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n然后你使用这样的模板make_lambda_to_call_member_function
:
auto fn = make_lambda_to_call_member_function(&Foo::x);\nfn(&foo);\n\nauto fn1 = make_lambda_to_call_member_function(&Foo::x1);\nfn1(&foo, 314);\n\nauto fn2 = make_lambda_to_call_member_function(&Foo::cx1);\nfn2(&foo, 44.832f);\n
Run Code Online (Sandbox Code Playgroud)\n\n但请注意,返回的 lambda 对象不会隐式转换为函数指针,因为 lambda 表达式使用 lambda 捕获。
\n\nC++0x 的最新草案 n3225 指出:
\n\n\n\n\n没有lambda 捕获的lambda 表达式的闭包类型具有一个公共非虚拟非显式 const 转换函数,用于指向具有与闭包类型的函数调用运算符相同的参数和返回类型的函数。此转换函数返回的值应是函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果。
\n
以下行为是非法的:
\n\nvoid (*fn5)(Foo*) = make_lambda_to_call_member_function(&Foo::x);\n
Run Code Online (Sandbox Code Playgroud)\n