boost::signals2
を使用して、C++アプリケーションでイベント通知を処理したいと考えています。私はブラウザのDOMイベントと同様の機能を持つもの、具体的にはイベントの伝播を止めて現在のレシーバがシグナルについて知る最後のものであり、その後のレシーバが呼び出されないようにすることを望んでいます。 (ブラウザでのこの動作の詳細については、http://www.w3.org/TR/DOM-Level-3-Events/#events-event-type-stopImmediatePropagationを参照してください)ブースト信号2の信号伝播を例外なくキャンセルする方法はありますか?
thingHappened
という信号を持つ仮想クラスApp
があります。 1つのApp
インスタンスが1つだけ存在する可能性があり、connect
〜thingHappened
のThingEvent
通知を受け取るさまざまなタイプのいくつかの他の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
いいですね、これを試してみましょう、ありがとう! – RandomEtc
コンバイナーの仕組みを見て、ありがとう!私は、shared_ptrに戻り、同じイベントのシグナルを呼び出す前にapp.thingHappened.set_combiner(MyCombiner(event))を使用すると、ThingEventでstopPropagationメソッドを使用できると思います。私は、これは問題ないと思うので、別のスレッドからHappenedを呼び出す必要はありません。この実装を反映するために私のgithubリンクを更新しました。 –
RandomEtc