1

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?

Kevin
  • 785
  • 2
  • 10
  • 32

1 Answers1

2

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)
sehe
  • 374,641
  • 47
  • 450
  • 633