Tôi nghĩ rằng kỹ thuật duy nhất hoạt động hoàn hảo là viết hàm bao bọc C cho mỗi hàm thành viên mà bạn muốn gọi trong cuộc gọi lại C; tức là .:
extern "C" void Foo_x(Foo *foo)
{
foo->x();
}
extern "C" void Foo_x1(Foo *foo, int i)
{
foo->x1(i);
}
Bạn cũng có thể sử dụng biểu thức lambda của C++ 0x, mà mặc nhiên chuyển sang một con trỏ đến chức năng có các thông số tương tự và kiểu trả về như hàm operator gọi kiểu đóng của. Tuy nhiên, hãy nhớ rằng liên kết ngôn ngữ của loại hàm là "C++" chứ không phải "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;
}
Lưu ý rằng Phần 5.2.2, Chức năng cuộc gọi, cáC++ Chuẩn C khẳng định:
Gọi một hàm thông qua một biểu thức có loại chức năng có mối liên hệ ngôn ngữ khác với ngôn ngữ của mối liên kết loại hàm của định nghĩa hàm được gọi là không xác định.
Vì vậy, sau về mặt kỹ thuật gọi hành vi undefined:
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.
//...
EDIT: Sau một chút thử nghiệm, tôi đã đưa ra các chức năng template sau đó trở lambdas đó gọi hàm thành viên đưa ra:
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...);
};
}
Nếu Foo
được định nghĩa là:
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; }
};
Sau đó, bạn sử dụng mẫu make_lambda_to_call_member_function
như:
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);
Lưu ý, tuy nhiên, các đối tượng lambda trở sẽ không mặc nhiên chuyển sang một con trỏ hàm vì biểu thức lambda sử dụng một lambda-chụp.
Dự thảo mới nhất của C++ 0x, n3225, khẳng định:
Kiểu đóng cửa cho một lambda-biểu không có lambda-chụp có một tổ chức phi ảo const không rõ ràng công cộng hàm chuyển đổi thành con trỏ tới hàm có cùng tham số và kiểu trả về như toán tử gọi hàm của kiểu đóng. Giá trị được trả về bởi hàm chuyển đổi này sẽ là địa chỉ của một hàm, khi được gọi, có cùng tác dụng như gọi toán tử gọi hàm của kiểu đóng.
Sau đây là bất hợp pháp:
void (*fn5)(Foo*) = make_lambda_to_call_member_function(&Foo::x);
trình biên dịch gì bạn đang sử dụng? Nếu bạn có thể sử dụng C++ 0x, chỉ cần sử dụng lambda's được capture-less, chúng có thể được chuyển đổi thành con trỏ hàm. Ngoài ra, tại sao tự động phân bổ một cái gì đó trong 'main'? – GManNickG
Bỏ qua 'mới' - không quan trọng cho câu hỏi. Tôi sử dụng GCC 4.5 và ICC 11.1. Bạn không chắc chắn cách lambda giúp đỡ ở đây, vì 'Hàm lambda là các đối tượng hàm của một kiểu phụ thuộc thực hiện'. Trên thực tế, tôi không biết C++ 0x tốt, ví dụ mã được đánh giá cao. – klimkin
Đã bỏ phiếu để đóng vì câu hỏi này ** có thể phù hợp hơn với http://codereview.stackexchange.com/ –