2012-08-13 40 views
5

在一個非boost項目中,我有一個使用基於某個用戶操作(按鈕按下/釋放)的計時器的類。我想這個類是通用的,所以它需要回調用戶定義的操作。現在如何在不相關的類之間傳遞C++回調?

// 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); 
... 

,從另一個類(DialogClass)我可以使用TimerClass,但我不能傳遞函數指針的回調函數。這些功能不是靜態的。編譯器結束了抱怨:

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

一些研究,這指出了std::function()std::bind()但我不熟悉這些,並希望就如何解決一些這方面的指針。

解決方案:任何有興趣,這裏是最終溶液

// 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

由於隱含'this'參數,成員函數的回調行爲會有所不同。 – 2012-08-13 18:32:39

+0

不使這些方法成爲靜態的,你需要在定時器中保持對實例的引用。你在使用C++ 11嗎? – jli 2012-08-13 18:34:08

+0

我們沒有使用C++ 11,但是前面的一個,因爲我們有'tr1'函數集 – fduff 2012-08-13 18:42:47

回答

2

std::function的磚塊是一個多態函數對象,其可包裝起來的任何類型的可調用對象的具有特定簽名。在你的情況,你希望它採取任何參數和返回任何值,所以你可以定義:

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

std::bind允許您將調用對象適應不同的簽名之一,通過結合參數的參數。在你的情況,你想通過綁定到特定的對象來調用它,以適應一個成員函數:

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

注意,這些都在2011年推出,使舊的編譯器將不支持他們。在這種情況下,您可以使用非常相似的Boost或TR1庫,或者使您自己的可調用類包含指向成員函數的指針和對要調用它的對象的指針/引用。

+0

歡呼邁克指着我走向正確的方向。 – fduff 2012-08-14 09:21:50

2

您不能將指針傳遞給類的方法,只能使用普通函數。我建議挖掘到std::function(),因爲你使用VS2010,它支持它們。有一個很好的(和很長的)教程描述他們和更多here

+1

稍作澄清:這裏的問題是'timerCallback'被定義爲指向普通函數的指針,所以你不能在其中存儲一個指向成員函數的指針。如果它已被定義爲指向成員函數的指針,那麼你可以。但是,你必須有一個'this'指針來指定它。正如其他人所說,這就是爲什麼std :: bind是正確答案的原因。 – 2012-08-13 20:17:29

+0

是的,謝謝澄清。我應該提供這樣的第一個地方... :) – 2012-08-14 05:34:00

+0

感謝您的鏈接,確實很有趣。 – fduff 2012-08-14 09:19:13

3

老式的做法是讓您的回調函數接受額外的void*參數,爲此您傳遞一個指向您希望調用該函數的對象的指針。然後,您使用靜態成員函數進行回調,並讓它將指針指向適當的對象並調用真正的回調函數。

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

這是老式的嗎? 我經常使用它! – acraig5075 2012-08-13 19:31:35

+0

這非常類似C的方式來做到這一點。唯一的問題是回調需要知道對象類型'((CMyDlg *)object) - >'這違背了泛型類的用途。 – fduff 2012-08-14 10:32:53

1

您可以創建一個充當函數指針的多態模板類。

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 
}; 

要初始化仿函數對象:

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

,並使用它:

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

下面是一個例子:

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 

下面是關於仿函數一個很好的資源一起與我的實現 如上所述。 http://www.newty.de/fpt/functor.html#chapter4

+0

或者你可以使用'std :: bind',它有幾個優點:它更強大,它在標準庫中(從C++ 11開始),因此對未來的代碼維護者更容易識別,並且更便攜,否則負責維護它。 – 2012-08-14 11:56:43