2009-09-04 7 views
5

私はC++で書かれたカスタムメニュークラスを持っています。コードを読みやすい関数に分離するために、コールバックを使用しています。C++で使用可能なコールバックを実装する良い方法

私はメニューのホストにシングルトンを使用したくないので、コールバックに最初のパラメータとして渡される別のパラメータ(ターゲット)を提供しています。 。

登録-署名

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

登録の例

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

ハンドラ

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

の例は1つが、このアプローチで汚れ検討することもできはありますか?多分もっと良いものはありますか?

+0

あなたはすべきですここではコールバックとして静的メンバーメソッドを使用しないでください。extern "C"と宣言されている関数を使用しているだけです。使用しているコンパイラが(現在)静的メソッドを呼び出すために同じメソッドを使用している –

答えて

10

あなたのアプローチでは、コールバック関数はフリー関数またはクラスの静的メンバーでなければなりません。クライアントがメンバ関数をコールバックとして使用することはできません。これに対する1つの解決策は、コールバックのタイプとしてboost::functionを使用することです:

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

クライアントは、コールバックに渡すboost::bindまたはboost::lambdaを使用することができます。

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

別のオプションは可能になるboost::signalsを使用することです同じイベントに登録する複数のコールバック。

+0

私は使用します'MenuItem&'それ以外は完全に合意した。 – MSalters

+0

クラスの静的メンバーを汎用コールバックとして使用しないでください(定義済みのABIはありません)。 boost ::関数はインターフェイスを宣言するための一般的なものです(boost ::関数はインターフェイス演算子()を使用します)。私はあなたのインターフェイスがより明示的になるように、この場合は明示的にすることをお勧めします。これは、orsogufoで説明されているように不適切なメソッドを戻すことはできません(つまり、コールバックを検証するコンパイラを取得する) –

8

私はあなたのアプローチが好きです。登録署名が

AddItem(string s, IMenuEntry * entry); 

そしてメソッド実装

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

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

:一つの代替は、ある意味では、コールバックの「OO同等」であるインターフェースを宣言することであろう

インターフェイスのアプローチでは、紛失したthisポインタの「void *回避策」を回避できます。

1

関数ポインタのシグネチャが読みにくいという点を除いて、何も問題はありません。しかし、私はこれを達成するためにおそらくobserverパターンでしょう。

3

boost::bindをご覧ください。

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

私は非常にこのためboost::functionboost:bindを見てお勧めします。それを学ぶことで、あなたの機能を100倍も簡単に結びつけることができます。

+0

また、boost :: lambdaも忘れないでください。時にはコールバックなしであなたを離れさせることができます。 – EFraim

+0

boost :: signalsはこのタスクにはより良いです。 – Arpegius

1

this白紙をお読みください。パフォーマンス、ユーザビリティ、および他のトレードオフを非常に詳細に分析することによって、コールバックメカニズムのさまざまなテクニックを構築します。:-(

0

あなたは、これはあなたがコールバックを提供するために、C形式の関数やオブジェクトのインタフェースのいずれかを使用することができます。あなたのコールバックをカプセル化するfunctorを使用することができますけれども、私はそれをハード読み取りを発見した。