如何以及为什么会使用Boost signal2? [英] How and why one would use Boost signals2?

查看:191
本文介绍了如何以及为什么会使用Boost signal2?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

学习c ++并尝试熟悉一些模式。 signals2 doc 显然有很大的我可以用插槽和信号做的一些事情。我不明白的是应该使用什么类型的应用程序(用例)。



我正在考虑一个状态机调度更改事件。来自动态类型的背景(C#,Java等),您将使用事件调度程序或静态参考或回调。



使用跨类回调在c ++中有困难吗?这是为什么 signals2存在?



一个例子是一个文档/视图。这种模式如何更适合于使用函数向量和循环中的每一个,或者说在调用注册的侦听类实例中调用状态更改的lambda?

  class Document 
{
public:
typedef boost :: signals2 :: signal& void()> signal_t;

public:
Document()
{}

/ *连接一个插槽,每当
文本为附在文件上。 * /
boost :: signals2 :: connection connect(const signal_t :: slot_type& subscriber)
{
return m_sig.connect(subscriber);
}

void append(const char * s)
{
m_text + = s;
m_sig();
}

const std :: string& getText()const
{
return m_text;
}

private:
signal_t m_sig;
std :: string m_text;
};

  class TextView 
{
public:
TextView(Document& doc):m_document(doc)
{
m_connection = m_document.connect(boost :: bind(& TextView :: refresh,this));
}

〜TextView()
{
m_connection.disconnect();
}

void refresh()const
{
std :: cout<<< TextView:<< m_document.getText()<<的std :: ENDL;
}
private:
文件& m_document;
boost :: signals2 :: connection m_connection;
};


解决方案

Boost.Signals2 不仅仅是一个回调数组,它具有很多附加值。 IMO,最重要的一点是:


  1. 线程安全性:多个线程可以同时连接/断开/调用相同的信号,而不会引入种族条件。这在与异步子系统进行通信时非常有用,例如在自己的线程中运行的活动对象。

  2. 连接 scoped_connection 允许断开连接而不直接访问信号的句柄。请注意,这是断开无法连接的插槽的唯一方法,例如 boost :: function (或 std :: function ) 。

  3. 临时插槽阻止。提供一种干净的方法来临时禁用侦听模块(例如,当用户请求暂停在视图中接收消息时)。

  4. 自动槽寿命跟踪:信号自动断开过期插槽。考虑当一个插槽是一个引用由 shared_ptr 管理的不可复制对象的绑定器时的情况:

     的shared_ptr<听者GT; l = listener :: create(); 
    auto slot = bind(& listener :: listen,l.get()); //我们不希望aSignal_影响listener生命周期
    aSignal_.connect(your_signal_type :: slot_type(slot).track(l)); //但是确实要在销毁时自动断开


当然,可以自己重新实现所有上述功能使用一个函数向量,并循环调用等等,但问题是如何比 Boost更好.Signals2 。重新发明车轮很少是个好主意。


Learning c++ and trying to get familiar with some patterns. The signals2 doc clearly has a vast array of things I can do with slots and signals. What I don't understand is what types of applications (use cases) I should use it for.

I'm thinking along the lines of a state machine dispatching change events. Coming from a dynamically typed background (C#,Java etc) you'd use an event dispatcher or a static ref or a callback.

Are there difficulties in c++ with using cross-class callbacks? Is that essentially why signals2 exists?

One to the example cases is a document/view. How is this pattern better suited than say, using a vector of functions and calling each one in a loop, or say a lambda that calls state changes in registered listening class instances?

class Document
{
public:
    typedef boost::signals2::signal<void ()>  signal_t;

public:
    Document()
    {}

    /* Connect a slot to the signal which will be emitted whenever
      text is appended to the document. */
    boost::signals2::connection connect(const signal_t::slot_type &subscriber)
    {
        return m_sig.connect(subscriber);
    }

    void append(const char* s)
    {
        m_text += s;
        m_sig();
    }

    const std::string& getText() const
    {
        return m_text;
    }

private:
    signal_t    m_sig;
    std::string m_text;
};

and

class TextView
{
public:
    TextView(Document& doc): m_document(doc)
    {
        m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
    }

    ~TextView()
    {
        m_connection.disconnect();
    }

    void refresh() const
    {
        std::cout << "TextView: " << m_document.getText() << std::endl;
    }
private:
    Document&               m_document;
    boost::signals2::connection  m_connection;
};

解决方案

Boost.Signals2 is not just "an array of callbacks", it has a lot of added value. IMO, the most important points are:

  1. Thread-safety: several threads may connect/disconnect/invoke the same signal concurrently, without introducing race conditions. This is especially useful when communicating with an asynchronous subsystem, like an Active Object running in its own thread.
  2. connection and scoped_connection handles that allow disconnection without having direct access to the signal. Note that this is the only way to disconnect incomparable slots, like boost::function (or std::function).
  3. Temporary slot blocking. Provides a clean way to temporarily disable a listening module (eg. when a user requests to pause receiving messages in a view).
  4. Automatic slot lifespan tracking: a signal disconnects automatically from "expired" slots. Consider the situation when a slot is a binder referencing a non-copyable object managed by shared_ptrs:

    shared_ptr<listener> l = listener::create();
    auto slot = bind(&listener::listen, l.get()); // we don't want aSignal_ to affect `listener` lifespan
    aSignal_.connect(your_signal_type::slot_type(slot).track(l)); // but do want to disconnect automatically when it gets destroyed
    

Certainly, one can re-implement all the above functionality on his own "using a vector of functions and calling each one in a loop" etc, but the question is how it would be better than Boost.Signals2. Re-inventing the wheel is rarely a good idea.

这篇关于如何以及为什么会使用Boost signal2?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆