4
\$\begingroup\$

I have written a small class for implementing the observer pattern in C++(14). I would welcome any comments, corrections criticisms and suggestions.

Observable.h:

#include <functional>
#include <vector>

class Observable
{
public:
    using Callback = std::function<void(void)>;

    template<typename O,typename F>
    void AddObserver(O self, F f)
    {
        obs.push_back(Callback([self,f](){(self->*f)();}));
    }

    void NotifyObservers(){for(auto f: obs){f();}}

private:
    std::vector<Callback> obs{};
};

Sample Code

main.cpp

#include <iostream>    

#include "Observable.h"

class A : public Observable
{
    private:
        int a;
        std::string s;
};

class B : public Observable
{
public:
    void EventHappened()
    {
        std::cout << "B has observed an event\n";
    }
};

void bar()
{
    std::cout << "This will not work";
}

class C
{
public:
    explicit C(A& a,B& b) : m_a(a), m_b(b)
    {
        a.AddObserver(this,&C::ReactToEventFromA);
        b.AddObserver(this,&C::ReactToEventFromB);

        //a.AddObserver(this,bar); //This will Fail.
        //b.AddObserver(this,&B::EventHappened); //This will Fail.
    }

    void ReactToEventFromA()
    {
        std::cout << "C has observed the event from A\n";
    }

    void ReactToEventFromB()
    {
        std::cout << "C has observed the event from B\n";
    }

private:
        A& m_a;
        B& m_b;
};


int main()
{
    A a;
    B b;
    C c(a,b);

    a.AddObserver(&b,&B::EventHappened);

    a.NotifyObservers();
    b.NotifyObservers();
}

The basic idea is that, to preserve simplicity, when you want a class to be able to notify observers, you just inherit publicly from Observable. And you use the AddObserver method to register any member function of any object to observe the class.

This is just a simple quick mock up, therefore the signature of the callback has been hardcoded to void(void). It shouldn't be too difficult to add the ability to register more complex functions. But this is it for now.

\$\endgroup\$
6
\$\begingroup\$

I'm not sure we really need a base Observable class to inherit from anymore, because with lambdas and function type erasure, this pattern has become more or less trivial.

But if you want to do it that way, I think you can simplify and generalize it a bit more, by relaxing constraints in addObserver's signature (which I renamed attach because it's the canonical name if I'm not mistaken):

#include <iostream>
#include <functional>

class Observable {
    using Callback = std::function<void(void)>;
    std::vector<Callback> notifications;
public:
    void attach(Callback c) { notifications.push_back(c); }
    void notify() { for (auto& c : notifications) c(); }
};

struct Buzzer {
    void go() { std::cout << "drrrr... drrrr...\n"; }
};

struct Rooster {
    void sing() { std::cout << "Cocorico!\n"; }
};

struct Sun : public Observable {
    void rise() { std::cout << "Waoooh! the sun's rising!\n"; notify(); }
};

int main() {
    Sun sun;
    Buzzer buzzer;
    Rooster rooster;

    sun.attach([&buzzer] {buzzer.go();});
    sun.attach([&rooster] {rooster.sing();});
    sun.rise();
}
\$\endgroup\$
  • \$\begingroup\$ Nice. Like the example usage. \$\endgroup\$ – Martin York Jan 10 '18 at 19:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.