如何以及为什么要使用 Boost 信号 2?

2021-12-24 00:00:00 events c++ boost boost-signals2

学习c++并尝试熟悉一些模式.signals2 doc 显然有大量我可以用插槽和信号做的一系列事情.我不明白的是我应该将它用于哪些类型的应用程序(用例).

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.

我在思考状态机调度更改事件的思路.来自动态类型的背景(C#、Java 等),您将使用事件调度程序或静态引用或回调.

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.

在 C++ 中使用跨类回调有困难吗?这基本上是为什么signals2 存在的原因吗?

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

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

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

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 不仅仅是回调数组",它还有很多附加值.IMO,最重要的几点是:

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

  1. 线程安全:多个线程可以同时连接/断开/调用同一个信号,而不会引入竞争条件.这在与异步子系统(例如在其自己的线程中运行的活动对象)通信时特别有用.
  2. connectionscoped_connection 句柄允许在没有直接访问 信号 的情况下断开连接.请注意,这是断开无与伦比的插槽的唯一方法,例如 boost::function(或 std::function).
  3. 临时插槽阻塞.提供一种干净的方法来暂时禁用侦听模块(例如,当用户请求暂停接收视图中的消息时).
  4. 自动插槽寿命跟踪:信号自动与过期"插槽断开连接.考虑当插槽是引用由 shared_ptrs 管理的不可复制对象的绑定器时的情况:

  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

当然,人们可以自己使用函数向量并在循环中调用每个函数"等重新实现上述所有功能,但问题是它如何比 Boost.Signals2.重新发明轮子很少是个好主意.

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.

相关文章