Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observer Pattern Via Boost Signal2

So, I've read the docs for Boost.Signal2 and I have done a bit of googling and I just haven't quite found what I needed. What I have is a controller and a view concept. The Controller will be sending data to the View for it to render. What I want is my controller to call Controller::Update and trigger the OnUpdate function in the view.

  • The controller and view should be disjoint
  • Signals on the Controller can be emitted to execute Slots in the View

Here is code I have tried so far:

class Listener {
public:
    virtual void OnUpdate() {};
};

class View :Listener
{
public:
    View(void);
    ~View(void);
    virtual void OnUpdate() override;
};

void View::OnUpdate()
{
    std::cout << "Updating in View";
}

class Controller
{
public:
    Controller(void);
    ~Controller(void);
    void Update();
};

Controller::Controller(void)
{
    // Signal with no arguments and a void return value
    boost::signals2::signal<void ()> sig;
    sig.connect(boost::bind(&Listener::OnUpdate, this, _1));
    // Call all of the slots
    sig();
    system("pause");
}

This does not compile. I get error C2825: 'F': must be a class or namespace when followed by '::', but this is just because I'm using bind incorrectly.

Does anybody know how I could achieve what I want using signals/slots from boost?

like image 620
Josh Sanders Avatar asked Feb 09 '15 17:02

Josh Sanders


1 Answers

There are quite a number of misconceptions here. I recommend you start simpler.

  • the Listener base class probably needs a virtual destructor
  • you cannot bind Listener::OnUpdate to this inside the Controller class because Controller is not derived from Listener
  • You need to derive publicly from Listener
  • there is no argument, so you need to pass zero placeholders (_1 was out of place)

Here's a simple fixed-up sample

Live On Coliru

#include <boost/signals2.hpp>
#include <iostream>

class Listener {
public:
    virtual ~Listener() = default;
    virtual void OnUpdate() = 0;
};

class View : public Listener
{
public:
    View() = default;
    ~View() = default;
    virtual void OnUpdate() override {
        std::cout << "Updating in View\n";
    }
};

class Controller
{
    boost::signals2::signal<void ()> sig;
public:
    Controller() {
    }

    void subscribe(Listener& listener) {
        // Signal with no arguments and a void return value
        sig.connect(boost::bind(&Listener::OnUpdate, &listener));
    }

    void DoWork() const {
        // Call all of the slots
        sig();
    }

    void Update();
};

int main() {

    View l1, l2;
    Controller c;

    c.subscribe(l1);

    std::cout << "One subscribed:\n";
    c.DoWork();

    c.subscribe(l2);

    std::cout << "\nBoth subscribed:\n";
    c.DoWork();
}

Which prints:

One subscribed:
Updating in View

Both subscribed:
Updating in View
Updating in View

Computer, Simplify: Now C++ style

Perhaps a more compelling example in C++ would be:

Live On Coliru

#include <boost/signals2.hpp>
#include <iostream>

struct View {
    void OnUpdate() { std::cout << "Updating in View\n"; }
};

class Controller {
    using UpdateHandler = boost::signals2::signal<void()>;
    UpdateHandler sig;

  public:
    Controller() {}

    void subscribe(UpdateHandler::slot_type handler) { sig.connect(handler); }
    void DoWork() const { sig(); }
    void Update();
};

int main() {

    View l1;
    Controller c;
    c.subscribe(std::bind(&View::OnUpdate, &l1));
    c.subscribe([] { std::cout << "Or we can attach a random action\n"; });

    c.DoWork();
}

Which prints

Updating in View
Or we can attach a random action
like image 77
sehe Avatar answered Nov 14 '22 21:11

sehe