Simple observer pattern – C++11

This is an implementation of a simple observer pattern, using only C++ standard library facilities. It makes use of C++11 polymorphic function objects and formed the basis of an exercise I set for an introductory C++11 workshop given at work. We shall develop simple code elements to be used something like this:

int main()
{
  Subject s;
  Observer o1 = ....;
  Observer o2 = ....;
  Observer o3 = ....;

  s.registerObserver(EventA, o1);
  s.registerObserver(EventB, o3);
  s.registerObserver(EventD, o1);
  s.registerObserver(EventB, o2);
  s.registerObserver(EventC, o3);

  s.notify(EventB); // notify all observers registered for EventB
  s.notify(EventC); // notify all observers registered for EventC
}


The general structure has

  • Subject: accepts registration requests according to some key or event. Notifies observers reigstered for a given event.
  • Observers: register themselves to be notified by the observer for a given eventSubject
  • Events: Act as registration key and trigger for notification

First let’s deal with the event type. C++11 enums to provide us with an easy means to implement a type safe event class with a certain set of values:

enum class Event { RED, GREEN, BLUE, ORANGE, MAGENTA, CYAN };

Next, a possible public interface of the Subject. We will deal with the implementation details later.


class Observer; // a passible subscriber callback class

class Subject
{
 public:
  void registerObserver(const Event& event, const Observer& observer);
  void notify(const Event& event) const;
};

From this interface we can see that the Subject will accept an unspecified Observer entity, to be subscribed to a given Event value. Let’s assume for now that the only restriction on this callable entity is that its signature be void().

One thing that has not been addressed yet is how to implement different functionality in the Subscriber instances. The traditional observer pattern would have a Observer base class with a virtual notify() method, with concrete Observer derived types implementing different functionality. This places a restriction on the system: only types derived from Observer can be used. This introduces a tight coupling between the Subject and anything that might want to observe it. It would be nice to be able to have a more general solution, where Subject and observers know nothing of each other. In fact, there is no reason why observers should know anything of each other either. We will rid ourselves of these restriction by using C++11 polymorphic function objects.

To this effect, our Subject will store polymorphic function objects with the required signature void(). Here is a partial implementation:

#include  <functional> // for std::function
#include <vector>
#include <map>
#include <utility>     // for std::forward
class Subject
{
 public:
  template <typename Observer>
  void registerObserver(const Event& event, Observer&& observer)
  {
    observers_[event].push_back(std::forward<Observer>(observer));
  }
  // other methods as before
 private:
  std::map<Event, std::vector<std::function<void()>>> observers_;
};

We can see that Observer is a template parameter of the new registerObserver template member function. Since we are in a deduced context, we can use a universal reference for the template parameter, Observer&& to allow for perfect forwarding and benefit from move semantics. Since Event is not a deduced type, we would have to resort to overloading to achieve the same effect. This is beyond the scope of this post and merits an in depth explanation elsewhere, but will be applied to the final Queue.

The function itself simply stores the Observer in an std::vector of std::function<void()>>. If the argument to registerObserver is not convertible to such an std::function we get a compiler error. The class stores the containers of functions in a map, so that observers can be notified according to the Events they have been registered for.

All we need now is the implementation of the Subject::notify(const Event&) method. We can use a simple iteration using a range based for-loop:


class Subject
{
  ...
  void notify(const Event& event)
  {
    for (const auto& obs : observers_[event]) obs();
  }
  ...
};

By now we have a very simple Subject class, and we do not need a Observer class at all, because we can use a combinaiton of std::function and std::bind for anything. In this example, we use our Event enumeration class:

#include "Subject.h"   // for our Subject class
#include <functional>  // for std::function and std::bind
#include <iostream>

void foo(int i)
{
  std::cout << "foo( " <<  i << " )\n";
}

void bar() {
  std::cout << "bar()\n";
}

int main()
{

  Subject s;
  s.registerObserver(Event::GREEN, bar);
  s.registerObserver(Event::ORANGE, std::bind(foo, 42));
  s.registerObserver(Event::RED, std::bind(foo, 12345));

  s.notify(Event::GREEN);
  s.notify(Event::RED);
  s.notify(Event::ORANGE);

}

The coupling between subject and observer has been greatly reduced (currently the only requirement is that the observer be callable with return type void and no arguments). There remains an unnecessary coupling between the Event type and the Subscriber class. There is no reason why our Subject class should be restricted to working only with out Event type. In the interest of generality, we will remove that coupling by making Subject a class template:

#include <functional>  // for std::function
#include <utility>     // for std::forward and std::move
#include <map>
#include <vector>

template <typename Event>
class Subject
{
 public:

  template <typename Observer>
  void registerObserver(const Event& event, Observer&& observer)
  {
    observers_[event].push_back(std::forward<Observer>(observer));
  }

  template <typename Observer>
  void registerObserver(Event&& event, Observer&& observer)
  {
    observers_[std::move(event)].push_back(std::forward<Observer>(observer));
  }

  void notify(const Event& event) const
  {
    for (const auto& obs : observers_.at(event)) obs();
  }

 private:
  std::map<Event, std::vector<std::function<void()>>> observers_;
};

This allows us to use any event type that can be used as a key of an std::map. Note that we have used the C++11 range checked std::map::at member function in the notify() method, and made it const.

To illistrate the generic use of types as events, in this example we use std::strings to represent events:


int main()
{

  Subject<std::string> s;
  s.registerObserver("GREEN", bar);
  s.registerObserver("ORANGE", std::bind(foo, 42));
  s.registerObserver("RED", std::bind(foo, 12345));

  const std::string msg("Hello, RED!");
  s.registerObserver("RED", [&msg]{std::cout << msg << std::endl;});

  s.notify("GREEN");
  s.notify("RED");
  s.notify("ORANGE");
}

We have arrived at a fairly generic and minimal Subject type for use in an observer pattern. By using C++11 idioms and standard library components, we have arrived at a working implementation in only a few lines of clear code. Since we use polymorphic function objects to represent the observers, we have great flexibility in determining what “observes” the subject. We can pass bound member functions of user defined types, lambda expressions (as seen in the example above), static member or non-member functions, or user defined functor instances. Code samples can be found on github.

Advertisements

15 comments

  1. thank you for this useful and interesting article, written in a very clear way and even improved by version iterations with explanations.

    it should be also containing some sort of ‘unregisterObserver’ techniques as well, please! 🙂

    1. Hi Val, thanks for your comments. It is nice to hear that someone finds this useful. Concerning your suggestion, I have been thinking about how to reconcile an observer de-registration with the “simple” concept presented here. I had not come up with a non-intrusive means to do this. If you think of it, the observers are not required to have any identity, and I did not wanted to keep the number of requirements to a minimum. Perhaps I can make the registerObserver method return a key, that can be used by the called to unregister the observer at a later stage. I will play around with this idea and write an up-date if it is satisfactory.

    1. One idea would be to return a key to the caller upon registration. It would then be up to the caller to use this key to un-register their callback. Obviously the callbacks would have to be stored in some kind of map. See my answer to the first comment.

      1. Ah, should have read it more closely. Thanks! I tried both passing in a handle to the addObserver call and getting a handle back to use later and went with the latter approach, like you recommended. Thanks for this article, it’s a much nicer pattern than requiring the observer to subclass a specific class.

  2. I just found this article. Very nice.

    I do have a question that may relate to un observing. Why do you take a templated observer and then turn it into a std::function? Why not take a std::function? Then users can set the function to empty if they want to unregister and your evoke goes from obs() to if(obs) obs().

    1. The purpose it to keep this as general as possible on the caller side. In any case, the subject holds std::function values, so there would be no handle for the caller to un-register callbacks. The de-registration could be implemented by returning a key to the caller, who can then use this key to remove their callback when needed.

  3. Thanks for sharing this article — although I think I’ll stay with the standard inheritance approach in my current code.

    As far I understood, the advantage is in a reduced coupling between subject and observer: no inheritance from a given observer base class, and no specified function names (like “update”) in the derived classes (although still specified function signatures).

    One disadvantage, however, is in setting up copy and move constructors. With the inheritance approach, you can handle that once in the base class, whereas the function-approach requires specialization of the operators in each observer class.

    Is there a workaround for that? Moreover, are there further advantages I missed?

    1. The workaround is to let the compiler generate all the constructors and assignment operators for you. This is beyond the scope of this post, but my approach is to have dedicated types dealing with any hand-made copy and assignment operations, and then use them as members in the “business logic” code, which can then leverage the compiler-synthesized special member functions.

  4. Very nice code. In regards to the “unregister” questions below: if you’d use a std::list instead of the std::vector (which proably would be a good idea in this case anyways, as registration / unregistration is more efficient), you probably could just use the iterator of the element pushed back as “key” for unregistration.

    1. Thanks, that’s actually a really nice approach in terms of code economy. The only thing that may be inefficient is verifying whether an iterator passed as key to a de-register function is valid.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s