2012-08-13 21 views
5

非ブーストプロジェクトでは、特定のユーザーアクション(ボタンの押下/離し)に基づいてタイマーを使用するクラスがあります。私はこのクラスを一般的なものにしたいので、ユーザー定義のアクションのコールバックを受け取ります。無関係のクラス間で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'パラメータの動作が異なります。 –

+0

メソッドを静的にすることなく、タイマー内のインスタンスへの参照を保持する必要があります。 C++ 11を使用していますか?我々は、このために必要な機能を持っていたtr1'機能 – jli

+0

。場合にのみ、今、私はそう、私はきれいで、明確な、あなたの質問... – fduff

答えて

2

std::functionある特定の署名と呼び出し可能オブジェクトの任意の型を包むことができる多型関数オブジェクトです。あなたのケースでは、あなたが定義することができますので、それは、引数を取りませんし、何も値を返さないようにしたい:

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

std::bindあなたはパラメータに引数を結合することによって、異なる署名のいずれかに呼び出し可能オブジェクトを適応させることができます。古いコンパイラはそれらをサポートしませんので、これらは、2011年に導入されたことを

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

注:あなたのケースでは、あなたは上でそれを呼び出すために、特定のオブジェクトに結合することによって、メンバ関数を適応したいです。その場合、非常によく似たBoostライブラリやTR1ライブラリを使うか、またはポインタをメンバ関数にする呼び出し可能なクラスと、呼び出したいオブジェクトへのポインタ/参照を作ることができます。

+0

私を正しい方向に向けるためのマイクの声援。 – fduff

2

クラスのメソッドにポインタを渡すことはできません。単純な関数のみを渡すことができます。 std::function()に掘り下げることをお勧めします。VS2010を使用しているからです。それらを説明する素敵な(そして長い)チュートリアルがあります。hereです。

+1

少し明確化:ここでの問題は、 'timerCallback'は通常の関数へのポインタとして定義されていることですので、あなたはそれでメンバ関数へのポインタを格納することはできません。それがメンバ関数へのポインタとして定義されていれば、そうすることができます。しかし、あなたはそれを呼び出すためのどこかに 'this'ポインタを持たなければなりません。これは、std :: bindが正しい答えである理由です。 –

+0

ええ、説明してくれてありがとう。私は...最初の場所で、このようなを提供している必要があります:)リンクの –

+0

おかげで、非常に興味深い確かに。 – fduff

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

+0

これはC言語のような方法です。唯一の問題は、コールバックオブジェクト型 '((CMyDlg *)オブジェクトを)知っている必要があるということです - >'ジェネリッククラスの目的に反しています。 – fduff

1

関数ポインタとして機能するポリモフィックテンプレートクラスを作成できます。

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 

はここに沿ってファンクタについての素晴らしいリソースですファンクタのオブジェクトを初期化する

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

私が実装した上述した。メンバ関数のための http://www.newty.de/fpt/functor.html#chapter4

+0

または、 'std :: bind'を使うことができます。これはいくつかの利点があります:もっと強力で、標準ライブラリ(C++ 11以降)にあり、将来のメンテナのコー​​ドやポータル、それ以外は、それを維持する責任があります。 –

関連する問題