5
\$\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\$

1 Answer 1

7
\$\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\$
2
  • 1
    \$\begingroup\$ Nice. Like the example usage. \$\endgroup\$ Commented Jan 10, 2018 at 19:26
  • \$\begingroup\$ This is incomplete. Where is the unregister/detach method? And how will you determine which callback to remove from the notifications when you pass in a local lambda you wrote inside some function? How sun.detach([&buzzer] {buzzer.go();}); going to find and detach the respective lambda you added previously? And you won't call the detach method from the same main method as in this trivial example. It'll be a different method, maybe even in a different class. \$\endgroup\$
    – KulaGGin
    Commented Apr 27, 2022 at 9:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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