2009-09-04 46 views
5

Tôi có một lớp Menu tùy chỉnh được viết bằng C++. Để tách mã thành các hàm dễ đọc, tôi đang sử dụng Callbacks.Một cách hay để thực hiện Callbacks có thể sử dụng trong C++

Vì tôi không muốn sử dụng Singletons cho Trình chủ của Menu, tôi cung cấp một tham số (đích) khác sẽ được gọi lại làm tham số đầu tiên (một số cách giải quyết cho tham chiếu "này" bị thiếu) .

Đăng ký doanh nghiệp-Chữ ký

AddItem(string s, void(*callback)(void*,MenuItem*), void* target = NULL) 

Ví dụ về Đăng ký doanh nghiệp

menu->AddItem(TRANSLATE, "translate", &MyApp::OnModeSelected); 

Ví dụ về một Handler

/* static */ 
void MyApp::OnModeSelected(void* that, MenuItem* item) { 
    MyApp *self = (MyApp*)that; 
    self->activeMode = item->text; 
} 

Có bất cứ điều gì người ta có thể xem xét bẩn với phương pháp này? Có những cái tốt hơn không?

+0

Bạn nên Bạn không nên sử dụng các phương thức thành viên tĩnh như gọi lại ở đây, bạn chỉ nên sử dụng các hàm được khai báo bên ngoài "C", bạn chỉ cần may mắn khi trình biên dịch bạn đang sử dụng (hiện tại) sử dụng cùng một phương thức để gọi các phương thức tĩnh Các chức năng này không được đảm bảo theo tiêu chuẩn –

Trả lời

10

Cách tiếp cận của bạn yêu cầu chức năng gọi lại là các hàm miễn phí hoặc thành viên tĩnh của một lớp. Nó không cho phép khách hàng sử dụng các hàm thành viên như gọi lại. Một giải pháp này là sử dụng boost::function như kiểu gọi lại:

typedef boost::function<void (MenuItem*)> callback_type; 
AddItem(const std::string& s, const callback_type& callback = callback_type()); 

Khách hàng sau đó có thể sử dụng boost::bind hoặc boost::lambda để vượt qua trong callback:

menu->AddItem("Open", boost::bind(&MyClass::Open, this)); 

Một lựa chọn khác là sử dụng boost::signals cho phép nhiều cuộc gọi lại để đăng ký cho cùng một sự kiện.

+0

Tôi muốn sử dụng 'MenuItem &' nhưng hoàn toàn đồng ý. – MSalters

+0

Bạn không nên sử dụng các thành viên tĩnh của lớp như một cuộc gọi lại chung (họ không có một ABI được xác định). Hàm boost :: chỉ là sự tổng quát hóa việc khai báo một giao diện (hàm boost :: chỉ sử dụng toán tử giao diện()). Tôi muốn các giao diện của tôi rõ ràng hơn một chút trong trường hợp này để bạn không vượt qua một phương thức không thích hợp trở lại (tức là lấy trình biên dịch để xác thực lại gọi lại) như được mô tả bởi orsogufo –

8

Tôi thích cách tiếp cận của bạn. Một thay thế sẽ được tuyên bố một giao diện, mà là ở một nghĩa nào đó là "OO tương đương" của một callback:

class IMenuEntry { 
public: 
    virtual void OnMenuEntrySelected(MenuItem* item) = 0; 
}; 

Chữ ký đăng ký sẽ trở thành

AddItem(string s, IMenuEntry * entry); 

Và việc thực hiện phương pháp

class MyApp : public IMenuEntry { 
public: 
    virtual void OnMenuEntrySelected(MenuItem* item){ 
     activeMode = item->text; 
    } 
} 

Cách tiếp cận giao diện sẽ cho phép bạn tránh khoảng trống "cách ly" đối với con trỏ this bị thiếu.

1

Tôi không thấy điều gì sai trái ngoại trừ chữ ký con trỏ hàm khó đọc. Nhưng, tôi có lẽ sẽ là observer mẫu để đạt được điều này.

3

Bạn có thể xem qua sử dụng boost::bind.

menu->AddItem(TRANSLATE, 
       "translate", 
       boost::bind(&MyApp::OnModeSelected, this, _1, _2)); 
1

Tôi khuyên bạn nên xem xét boost::functionboost:bind cho việc này. Học nó sẽ làm cho chức năng của bạn gắn kết dễ dàng hơn một trăm lần.

+0

cũng đừng quên tăng cường :: lambda. Đôi khi nó có thể cho phép bạn đi mà không cần gọi lại! – EFraim

+0

boost :: các tín hiệu tốt hơn cho nhiệm vụ này – Arpegius

1

Đọc this giấy trắng. Nó xây dựng các kỹ thuật khác nhau cho một cơ chế gọi lại bằng cách phân tích hiệu suất, khả năng sử dụng và các sự cân bằng khác một cách chi tiết.Tôi thấy nó đọc cứng mặc dù :-(

0

của bạn có thể sử dụng một functor để đóng gói callback của bạn. Điều này sẽ cho phép bạn sử dụng hoặc một hàm C-phong cách hay một giao diện đối tượng để cung cấp gọi lại.

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