0

我想boost::signals2用来处理我的 C++ 应用程序中的事件通知。我希望实现与浏览器 DOM 事件具有相似功能的东西,特别是能够停止事件传播的能力,以便当前接收器是最后一个知道信号的接收器,并且不会调用后续接收器。(请参阅http://www.w3.org/TR/DOM-Level-3-Events/#events-event-type-stopImmediatePropagation了解更多关于它是如何在浏览器中工作的)

我有一个App带有信号的假设类,称为thingHappened. 很可能只有一个App实例,其他几个Widget不同类型的类将connect接收thingHappened通知ThingEvent。有时小部件想要消耗(停止) sThingEvent以便不Widget通知其他 s。

起初我想知道我是否可以通过 a 来实现这一点,shared_connection_block但现在我明白这一次只会抑制一个连接。最初我将 a 传递shared_ptr<ThingEvent>给我的信号,但是一旦调用了信号,就无法干预它的传播。如果我传递一个 shared_ptr,我可以让信号接收器检查事件的值并在它已设置时返回,但我不想将该细节推送给我的库的用户。

我找到的解决方案是ThingEvent在堆栈上传递 a 以便为每个接收器复制它。如果我设置mStopPropagation了事件,那么当它被销毁时,我可以抛出异常并且信号调用终止。这样做的缺点是我在调用信号的时候需要我自己的 try/catch,而且从风格上讲,这意味着我将 anexception用于非特殊目的。有没有更好的办法?

这是我假设的App课程,带有一个信号thingHappened

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

我的ThingEvent类,有一些关于事件的数据(例如类型)和一个mStopPropagation属性,如果在析构函数中设置它会导致抛出异常:

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;

};

这是一个示例信号使用者 a ,如果类型为Widget,它将调用stopPropagation()a :event"goat"

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()函数:

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

如果你手头有提升(我使用的是 1.44)并且你想编译它,完整的代码和 Makefile 在https://gist.github.com/1445230

4

1 回答 1

2

您可以使用自定义信号组合器来执行此操作。这些在 boost.signals 教程的“信号返回值(高级)”部分中进行了解释:http: //www.boost.org/doc/libs/1_48_0/doc/html/signals/tutorial.html#id3070223

这是它的要点:

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

operator() 的输入迭代器指的是信号槽的集合。每次取消引用其中一个迭代器时,它都会调用插槽。因此,您可以让其中一个插槽返回一个值,以指示它不希望调用任何其他插槽。

然后,您只需将其传递给信号的第二个模板 arg:

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

现在您可以实现 thingHappened 以返回一个布尔值,指示您是否希望信号停止。

于 2011-12-08T21:49:34.610 回答