Я думаю, что единственная техника, которая будет работать идеально, - это написать функцию-обертку C для каждой функции-члена, которую вы хотите вызвать в обратном вызове C; i.e.:
extern "C" void Foo_x(Foo *foo)
{
foo->x();
}
extern "C" void Foo_x1(Foo *foo, int i)
{
foo->x1(i);
}
Вы также можете использовать лямбда-выражения C ++ 0x, которые неявно преобразуются в указатель на функцию, имеющую тот же параметр и возвращаемый тип, что и оператор вызова функции закрытого типа. Но имейте в виду, что языковой связкой типа функции является «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;
}
Обратите внимание, что в разделе 5.2.2 «Вызов функции» стандарта C ++ говорится:
Вызов функции через выражение, тип функции которого имеет языковую связь, которая отличается от языковой связи типа функции определения вызываемой функции, не определена.
Таким образом, следующее технически вызывает неопределенное поведение:
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.
//...
РЕДАКТИРОВАТЬ: После нескольких экспериментов, я придумал следующие шаблонные функции, которые возвращают лямбда-выражения, которые вызывают данную функцию-член:
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);
Обратите внимание, однако, что возвращаемые лямбда-объекты не будут неявно преобразованы в указатель функции, потому что лямбда-выражение использует лямбда-захват.
В последней версии C ++ 0x, n3225 говорится:
Тип закрытия для лямбда-выражения без лямбда-захвата имеет общедоступную не виртуальную неявную функцию преобразования констант в указатель на функцию с таким же параметром и возвращает типы как оператор вызова функции типа замыкания. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и вызов оператора вызова функции типа замыкания.
Недопустимо следующее:
void (*fn5)(Foo*) = make_lambda_to_call_member_function(&Foo::x);