2011-12-07 8 views
0

boost::signals2を使用して、C++アプリケーションでイベント通知を処理したいと考えています。私はブラウザのDOMイベントと同様の機能を持つもの、具体的にはイベントの伝播を止めて現在のレシーバがシグナルについて知る最後のものであり、その後のレシーバが呼び出されないようにすることを望んでいます。 (ブラウザでのこの動作の詳細については、http://www.w3.org/TR/DOM-Level-3-Events/#events-event-type-stopImmediatePropagationを参照してください)ブースト信号2の信号伝播を例外なくキャンセルする方法はありますか?

thingHappenedという信号を持つ仮想クラスAppがあります。 1つのAppインスタンスが1つだけ存在する可能性があり、connectthingHappenedThingEvent通知を受け取るさまざまなタイプのいくつかの他のWidgetクラスがあります。場合によっては、ThingEventを消費(停止)して、Widgetが通知されないようにすることがあります。

最初は私がshared_connection_blockでこれを達成できるかどうか疑問に思っていましたが、これは一度に1つの接続しか抑制しないことを理解しています。最初は私はshared_ptr<ThingEvent>を私の信号に渡しましたが、信号が呼び出されるとその伝播に介入する方法はありませんでした。私がshared_ptrを渡した場合、シグナルレシーバはイベントの値をチェックし、設定されていればリターンすることができますが、私のライブラリのユーザにその詳細をプッシュしたくありません。

私が見つけた解決策は、受信者ごとにコピーされるようにスタックにThingEventを渡すことです。イベントにmStopPropagationを設定すると、そのイベントが破棄されると例外がスローされ、シグナルコールが終了します。これの欠点は、シグナルが呼び出される時点で私自身のtry/catchが必要であり、文法的には例外的な目的のためにexceptionを使用しているということです。より良い方法がありますか?

class App 
{ 
public: 
    boost::signals2::signal<void (class ThingEvent)> thingHappened; 
}; 

マイThingEventクラス、イベントに関するいくつかのデータ(例えばタイプ)とmStopPropagationプロパティと例外があればスローされます:

はここで信号thingHappenedで、私の仮定のAppクラスですデストラクタに設定されている:

class ThingEvent 
{ 
public: 
    ThingEvent(string type): mType(type), mStopPropagation(false) { } 

    ~ThingEvent() 
    { 
     if (mStopPropagation) { 
      throw exception(); 
     } 
    } 

    void stopPropagation() { mStopPropagation = true; } 

    string getType() { return mType; } 

private: 
    string mType; 
    bool mStopPropagation; 

}; 

はここにstopPropagation()を呼び出すサンプル信号消費者、Widget、ですタイプは"goat"の場合:あなたが手にブーストを(私は1.44を使用しています)があり、あなたがしたい場合は

int main() 
{ 
    App app; 

    Widget w1("1"); 
    Widget w2("2"); 
    Widget w3("3"); 

    boost::signals2::connection c1 = app.thingHappened.connect(boost::bind(&Widget::thingHappened, &w1, _1)); 
    boost::signals2::connection c2 = app.thingHappened.connect(boost::bind(&Widget::thingHappened, &w2, _1)); 
    boost::signals2::connection c3 = app.thingHappened.connect(boost::bind(&Widget::thingHappened, &w3, _1)); 

    // all three widgets will receive this 
    app.thingHappened(ThingEvent("otter")); 

    { 
     // suppress calls to c2 
     boost::signals2::shared_connection_block block(c2,true); 
     // only w1 and w3 will receive this 
     app.thingHappened(ThingEvent("badger")); 
    } 

    // Widgets call ThingEvent::stopPropagation() if the type is "goat" 
    try { 
     // only w1 will receive this 
     app.thingHappened(ThingEvent("goat")); 
    } catch (exception &e) { 
     // ThingEvent's destructor throws if mStopPropagation is true 
     std::cout << "exception thrown by thingHappened(goat)" << std::endl; 
    } 

    return 0; 
} 

class Widget 
{ 
public: 
    Widget(string name): mName(name) {} 

    ~Widget() {} 

    void thingHappened(ThingEvent thing) 
    { 
     cout << thing.getType() << " thingHappened in widget " << mName << endl; 

     if (thing.getType() == "goat") { 
      thing.stopPropagation(); 
     } 
    } 

    string getName() 
    { 
     return mName; 
    } 

private: 
    string mName; 
}; 

は最後に、ここでは、これらのクラスを使用して迅速main()機能ですそれをコンパイルすると、完全なコードとMakefileは、https://gist.github.com/1445230

答えて

2

です。カスタム信号結合器でこれを行うことができます。 http://www.boost.org/doc/libs/1_48_0/doc/html/signals/tutorial.html#id3070223

ここでの要点です:これらはboost.signalsチュートリアルの「信号戻り値(アドバンス)」セクションで説明されている

struct MyCombiner { 
    typedef bool result_type; 
    template <typename InputIterator> result_type operator()(InputIterator aFirstObserver, InputIterator aLastObserver) const { 
     result_type val = false; 
     for (; aFirstObserver != aLastObserver && !val; ++aFirstObserver) { 
      val = *aFirstObserver;    
     } 
     return val; 
    } 
}; 

入力イテレータオペレータには、()のコレクションを参照してください。信号のためのスロットの数。イテレーターの1つを逆参照するたびに、それはスロットを呼び出します。したがって、スロットの1つに値を返して、それ以上のスロットを呼びたくないことを示すことができます。

その後、あなたは自分の信号の第2のテンプレート引数にそれを渡します

boost::signals2::signal<bool(ThingEvent), MyCombiner> sig; 

今あなたが信号を停止するかどうかを示すブール値を返すようにthingHappenedを実装することができます。

+0

いいですね、これを試してみましょう、ありがとう! – RandomEtc

+0

コンバイナーの仕組みを見て、ありがとう!私は、shared_ptr に戻り、同じイベントのシグナルを呼び出す前にapp.thingHappened.set_combiner(MyCombiner(event))を使用すると、ThingEventでstopPropagationメソッドを使用できると思います。私は、これは問題ないと思うので、別のスレッドからHappenedを呼び出す必要はありません。この実装を反映するために私のgithubリンクを更新しました。 – RandomEtc

関連する問題