Boost Signals2 将 Slot 传递给成员函数以进行断开连接

Boost Signals2 pass Slot to member Function for Disconnecting

我有以下 class,它使用简单的 boost::Signals2::Signal:

class Button {
    using OnClick = signal<void()>;

   public:
    using OnClickSlotType = OnClick::slot_type;
    boost::signals2::connection add_handler(const OnClickSlotType& slot) {
        return click.connect(slot);
    }

    void remove_handler(const OnClickSlotType& slot) {
        std::cout << "Disconnect\n";
        click.disconnect(&slot);
    }

    signal<void()> click;
};

我使用 class 如下:

void demo() { std::cout << "Demo called\n"; }

void second() { std::cout << "Second\n"; }

int main() {
    Button btn;

    btn.add_handler(&demo);
    btn.add_handler(&second);

    btn.click();

    btn.remove_handler(&demo);

    btn.click();
}

但是函数demo没有断开。输出总是:

Demo called
Second
Disconnect
Demo called
Second

如何正确断开功能与信号的连接?

您可以多次注册相同的功能,从而导致多个连接。

因此,函数不充分标识。

相反,您可以使用 connection 对象断开特定连接:

Live On Coliru

auto d = btn.add_handler(&demo);
btn.add_handler(&second);

btn.click();

d.disconnect();

btn.click();

版画

Demo called
Second
Disconnect doesn't require access to either source or subscriber
Second

这样做的好处在于它解耦了源、订阅者和连接。您可以拥有 table 个连接并断开它们,而无需了解相关方。

奖金:scoped_connection

作用域连接是连接的 RAII 包装器。这意味着您可以以与包装器的生命周期相关的异常安全方式断开连接。这对于防止生命周期问题非常有用:

Live On Coliru

#include <boost/signals2/signal.hpp>
#include <iostream>
#include <optional>
using boost::signals2::signal;

class Button {
    using OnClick = signal<void(std::string const&)>;

   public:
    using OnClickSlotType = OnClick::slot_type;
    boost::signals2::connection add_handler(const OnClickSlotType& slot) {
        return click.connect(slot);
    }

    OnClick click;
};

struct Demo {
    Demo(Button& btn, std::string name)
        : _connection(btn.add_handler(std::ref(*this))),
          _name(std::move(name))
    { }

    Demo(Demo const&) = delete;
    Demo(Demo&&) = delete;

    void operator()(std::string const& msg) const {
        std::cout << _name << " called (" << msg << ")\n";
    }
  private:
    boost::signals2::scoped_connection _connection;
    std::string _name;
};

int main() {
    Button btn;

    std::optional<Demo> foo;
    {
        Demo bar(btn, "bar");

        btn.click("first click L:" + std::to_string(__LINE__));

        foo.emplace(btn, "foo");

        btn.click("second click L:" + std::to_string(__LINE__));

        foo.reset();

        btn.click("third click L:" + std::to_string(__LINE__));
    } // bar is disconnecteded

    // no connections left
    btn.click("last click L:" + std::to_string(__LINE__));
}

版画

bar called (first click L:42)
bar called (second click L:46)
foo called (second click L:46)
bar called (third click L:50)