简体   繁体   中英

Boost Signals2 pass Slot to member Function for Disconnecting

I have the following class which uses a simple 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;
};

I use the class like the following:

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

But the Function demo is not disconnected. The output is always:

Demo called
Second
Disconnect
Demo called
Second

How can i correctly disconnect the Function from the Signal?

You could register the same function more than once, resulting in multiple connections.

Therefore, the function is not sufficient identity.

Instead, you can use the connection object to disconnect a particular connection:

Live On Coliru

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

btn.click();

d.disconnect();

btn.click();

Prints

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

The beauty of this is that it decouples sources, subscribers and connections. You could have a table of connections and disconnect them without ever needing to know the parties involved.

BONUS: scoped_connection

Scoped connections are a RAII wrapper for connections. This means that you can have connections disconnect in an exception safe manner tied to the life-time of a wrapper. This is awesome for protecting against lifetime issues:

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

Prints

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM