2012-08-13 21 views
5

Trong dự án không tăng cường, tôi có một lớp sử dụng bộ đếm thời gian dựa trên hành động của người dùng nhất định (nút được nhấn/phát hành). Tôi muốn lớp này chung chung, do đó, nó sẽ gọi lại cho các hành động do người dùng xác định.cách chuyển C++ callback giữa các lớp không liên quan?

// TimerClass.h 
typedef void (*timerCallback)(void); 
... 
Class TimerClass : public CButton { 
public: 
    void setCallbackShortTime(timerCallback clickFn) { shortCallback = clickFn;} ; 
    void setCallbackLongTime(timerCallback clickFn) { longCallback = clickFn;} ; 
private: 
    timerCallback shortCallback, longCallback; 
} 


// CMyDlg.h 
class CMyDlg : public CDialog 
{ 
public: 
    void openLiveViewWindow(); 
    void openAdminPanelWindow(); 
    TimerClass _buttonSettings; 
} 

// CMyDlg.cpp 
... 
_buttonSettings.setCallbackShortTime(&openLiveViewWindow); 
... 

Bây giờ, từ một lớp khác (DialogClass) Tôi có thể sử dụng TimerClass nhưng tôi không thể chuyển con trỏ hàm đến chức năng gọi lại. Các hàm này không tĩnh. Trình biên dịch kết thúc lên phàn nàn:

error C2276: '&' : illegal operation on bound member function expression 

Một số nghiên cứu về vấn đề này chỉ ra cho std::function()std::bind() nhưng tôi không quen với những điều này và sẽ đánh giá cao một số gợi ý về cách giải quyết này.

GIẢI: Đối với bất cứ ai quan tâm, đây là những viên gạch của giải pháp cuối cùng

// TimedButton.h 
#include <functional> 
// define timer callback prototype 
typedef std::function<void()> timerCallback; 
... 
class TimedButton : public CButton 
{ 
public: 
    TimedButton(); 
    ... 
    void setCallbackShortTime(timerCallback clickFn) { _shortCallback = clickFn;} ; 
    void setCallbackLongTime(timerCallback clickFn) { _longCallback = clickFn;} ; 
private: 
    timerCallback _shortCallback; 
    timerCallback _longCallback; 
} 

// TimedButton.cpp 
... 
(_longCallback)(); // call long duration fn 
... 
(_shortCallback)();  // call short duration fn 

// in MyDlg.cpp 
#include <functional> 
... 
_buttonSettings.setCallbackShortTime(std::bind(&CMyDlg::openLiveViewWindow, this)); 
_buttonSettings.setCallbackLongTime(std::bind(&CMyDlg::openAdminPanelWindow, this)); 
+0

gọi lại cho các chức năng thành viên hoạt động khác nhau do tham số 'this' ẩn. –

+0

Nếu không thực hiện các phương thức tĩnh, bạn cần giữ tham chiếu đến cá thể trong bộ hẹn giờ. Bạn đang sử dụng C++ 11? – jli

+0

chúng tôi không sử dụng C++ 11, nhưng trước đó chúng ta có tập hợp các chức năng 'tr1' – fduff

Trả lời

2

std::function là một đối tượng chức năng đa hình, có thể quấn lên bất kỳ loại đối tượng có thể được gọi với một chữ ký riêng. Trong trường hợp của bạn, bạn muốn nó không có đối số và trả lại không có giá trị, vì vậy bạn có thể định nghĩa:

typedef std::function<void()> timerCallback; 

std::bind cho phép bạn điều chỉnh một đối tượng có thể được gọi đến một trong một chữ ký khác nhau, bằng cách liên kết các đối số cho các tham số. Trong trường hợp của bạn, bạn muốn thích nghi với một hàm thành viên bằng cách gắn nó vào một đối tượng cụ thể để gọi nó vào lúc:

_buttonSettings.setCallbackShortTime(std::bind(&CMyDlg::openLiveViewWindow, this)); 

Lưu ý rằng những đã được giới thiệu vào năm 2011, vì vậy trình biên dịch cũ sẽ không hỗ trợ họ. Trong trường hợp đó, bạn có thể sử dụng các thư viện Boost hoặc TR1 rất giống nhau, hoặc tạo lớp có thể gọi của riêng bạn có chứa hàm con trỏ thành thành viên và một con trỏ/tham chiếu tới đối tượng bạn muốn gọi nó.

+0

cổ vũ Mike vì đã chỉ cho tôi đúng hướng. – fduff

2

Bạn không thể chuyển con trỏ đến một phương thức của một lớp, chỉ các hàm thuần túy. Tôi khuyên bạn nên đào sâu vào std::function(), vì bạn đang sử dụng VS2010, hỗ trợ chúng. Có một hướng dẫn tốt đẹp (và dài) mô tả chúng và nhiều hơn nữa here.

+1

Một chút làm rõ: vấn đề ở đây là 'timerCallback' được định nghĩa là một con trỏ đến một hàm bình thường, vì vậy bạn không thể lưu một con trỏ tới hàm thành viên trong đó. Nếu nó đã được định nghĩa là một con trỏ đến hàm thành viên, thì bạn có thể. Nhưng sau đó bạn phải có một con trỏ 'this' ở đâu đó để gọi nó. Đó là lý do tại sao std :: bind là câu trả lời đúng, như những người khác đã nói. –

+0

Vâng, cảm ơn vì đã làm rõ. Tôi nên đã cung cấp như vậy ở nơi đầu tiên ... :) –

+0

cảm ơn cho các liên kết, rất thú vị thực sự. – fduff

3

Cách cũ để thực hiện việc này là làm cho chức năng gọi lại của bạn chấp nhận tham số void* bổ sung, cho phép bạn chuyển con trỏ đến đối tượng bạn muốn gọi hàm. Sau đó, bạn sử dụng một hàm thành viên tĩnh cho cuộc gọi lại và để cho nó truyền con trỏ tới đối tượng thích hợp và gọi lại gọi lại thực sự của bạn.

typedef void (*timerCallback)(void*); 
... 
void setCallbackShortTime(timerCallback clickFn, void* object) { shortCallback = clickFn; shortCallbackObject = object;} ; 
void setCallbackLongTime(timerCallback clickFn, void* object) { longCallback = clickFn; longCallbackObject = object;} ; 
... 

static void openLiveViewWindowCallback(void* object) { ((CMyDlg*)object)->openLiveViewWindow(); } 
void openLiveViewWindow(); 
+0

Đây có phải là lỗi thời không? Tôi đã sử dụng nó thường xuyên! – acraig5075

+0

đó là một cách rất giống với C để thực hiện điều đó. Vấn đề duy nhất là callback cần biết kiểu đối tượng '((đối tượng CMyDlg *)) ->' chống lại mục đích của một lớp generic. – fduff

1

Bạn có thể tạo lớp mẫu đa hình hoạt động như con trỏ hàm.

class TFunctorBase 
{ 
public: 
    TFunctorBase(void) {} 
    virtual ~TFunctorBase(void) {} 
    virtual void operator()(void) = 0; 
}; 

// derived template class 
template <class TClass> class TFunctor : public TFunctorBase 
{ 
private: 
    void (TClass::*fpt)(); // pointer to member function 
    TClass* pt2Object;     // pointer to object 

public: 
    TFunctor(TClass* _pt2Object, void(TClass::*_fpt)()) 
    { pt2Object = _pt2Object; fpt = _fpt;}; 

    virtual void operator()(void) 
    { (*pt2Object.*fpt)();};    // execute member function 
}; 

Để khởi tạo một đối tượng functor:

TFunctor<MyClass> obj(&myInstance, &MyClass::myMemberFunction); 

Và để sử dụng nó:

(*obj)(); 
//(*obj)(42); for operator()(int) 

Dưới đây là một ví dụ:

class ClassA 
{ 
public: 
    void function1(int a, string b); 
}; 

ClassA objA; 
TFunctor<ClassA> functor(&objA,&ClassA::function); 
(*functor)(42, "pumpkin"); //assuming you added virtual void operator()(int, string) to TFunctorBase 

Dưới đây là một nguồn lực lớn về functors cùng với việc triển khai miêu tả trên. http://www.newty.de/fpt/functor.html#chapter4

+0

Hoặc bạn có thể sử dụng 'std :: bind', có nhiều ưu điểm: nó mạnh hơn rất nhiều, trong thư viện chuẩn (bắt đầu với C++ 11) do đó dễ nhận biết hơn đối với những người duy trì mã của bạn trong tương lai và di động hơn, và ai đó người khác chịu trách nhiệm duy trì nó. –

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