2011-01-31 27 views
5

Dưới đây tôi có khái niệm về hàm thành viên lớp ràng buộc với hàm toàn cầu. Mục đích chính của việc này là sử dụng C++ để thực hiện chức năng gọi lại kiểu C. Điều này có thể được thực hiện theo cách tốt hơn (ví dụ, không có macro cuối cùng hoặc typeof hoặc sử dụng các tính năng C++ 0x) không?Chức năng thành viên của lớp kết nối với c-function

#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

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

+0

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

+0

Đã bỏ phiếu để đóng vì câu hỏi này ** có thể phù hợp hơn với http://codereview.stackexchange.com/ –

Trả lời

1

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); 
+0

Đó là một điểm tốt về loại liên kết. Tôi thấy một vấn đề với việc sử dụng lambda - tất cả các loại đối số nên được liệt kê đầy đủ. Với phiên bản mẫu tất cả các loại được tính toán với trình biên dịch. Có thể tạo một mẫu hàm lambda, hàm này sẽ lấy hàm thành viên làm tham số không? – klimkin

+0

@klimkin: Tôi đã cập nhật câu trả lời của mình với nhiều thông tin hơn. Để trả lời câu hỏi của bạn, có thể viết một hàm mẫu trả về một đối tượng lambda gọi một hàm thành viên đã cho, nhưng đối tượng lambda đó không thể được chuyển đổi thành một con trỏ hàm nữa. –

Các vấn đề liên quan