The Observer pattern revisited

I wish the observer pattern was as cool as the Protoss Observer!
I always thought the Observer pattern was one of the easy ones. A few years ago, while working on a legacy C++ product, I found an incarnation of the Observer pattern that made the evil observers from Fringe look like your best friends. There were many issues with the code involved. The worst one was probably the avalanche of notifications as a result of a single simple notification. I’m still not sure if the programmers were big fans of wispy italian food or just plain masochistic.

This won’t be a rant about what is wrong with the Observer pattern but it’s about two improvements that one can make applying the pattern in C++.

A few things irked me while using the legacy code for creating Subjects and Observers.

  1. To use the code you had to link with <drumroll> common.dll. A superbly named library such as this inevitably becomes bloated and adds a lot of unnecessary link time. The solution described here is header-only. I know this tends to increase compile times but it’s much easier to use if you just need an Observer.
  2. You can only notify an observer. There is no way to send some extra information about the event that occurred. The above-mentioned masochists made sure that loose coupling, which the Observer pattern is supposed to promote, is destroyed by making you jump through hoops to get the state change of one tiny int. Adding a parameter to the notification of course meant subclassing both the Subject as well as the Observer class. Object oriented programming is using inheritance everywhere right?
  3. Poor lifetime management. In my experience Java programmers tend to ignore thinking about the lifetime of their objects. Most probably because of the, admittedly excellent, garbage collector. It only cleans up memory though, and Java just is not C++. Some programs tend to be rather static; once the object structures are built they don’t change until program shutdown. And who cares what happens at program shutdown right? There were many difficult to find bugs related to deleted observers still getting notifications (Hello, NPE, my old friend). There were also errors where a diligent developer did try to unregister an observer but the actual subject was already freed.

First I’ll show how to use this code and then I’ll describe some of the implementation. The idea is that we can define the ‘type’ of notification when we create the Subject. The Subject object should preferably be stored as a member variable of the actual object we want to observe. There is no real Observer class or interface. You basically just register anything that fits the notification type and you receive a Registration. Both Subject and Registration will do clean up in their destructor like proper C++ citizens. Enough talk, here is a simple example:

void react()
{
  cout << "I received something interesting!" << endl;
}

int main()
{
  Subject<void ()> iAmInteresting;

  auto registration = iAmInteresting.registerObserver(&react);

  iAmInteresting();
}

If you are familiar with boost::function or std::function then this syntax is not surprising. We template the Subject with the signature of the notify function. When you register the react() function as an observer you receive a Registration (I really love some parts of C++11, auto ftw!). When the Registration goes out of scope it unregisters itself with the Subject. Note that instead of the classical notify()/update() functions we just use the C++ call operator to notify observers. Here’s a more interesting example which includes a lambda!

struct HubbleTelescope
{
  void foundNewPlanet(const int type, const string name) const
  {
    cout << "The hubble telescope found a new planet of type " 
         << type << " named " << name << endl;
  }
};

int main()
{
  Subject<void (int, string)> space;

  // E.T. is watching space
  auto E_T_ = space.registerObserver( [] (int, string name) 
    {
      cout << "E.T. phone " << name << "?" << endl;
    });

  {
    // The hubble telescope is watching only a portion of space
    HubbleTelescope ht;
    auto registration = space.registerObserver(
      bind(&HubbleTelescope::foundNewPlanet, cref(ht), _1, _2));

    space(2, "Mogo"); 
  } // the Hubble stops being interested here.

  space(3, "Oa");
}

This will print:
E.T. phone Mogo?
The hubble telescope found a new planet of type 2 named Mogo
E.T. phone Oa?

So we see that an observer is automatically unregistered but we can still notify the remaining observers. Of course we would use a lambda for registering the telescope as well, instead of that ugly bind.

The code for all of this is only around 40 lines, give or take (not counting comments and lines with closing curly braces). We need to know whether an object is still alive or not. To do this we leverage the awesome std::shared_ptr and std::weak_ptr. The Subject has a ‘heartbeat’ inside of it. When unregistering we check a weak_ptr to this heartbeat to see if the Subject is still there. The Subject holds a vector of observers, once a registration is deleted, it removes itself from this vector.

When I first wrote this code years ago, I did not have access to all the C++11 goodness. I used a lot of boost and repeated code to make it work. This implementation is purely ‘std’. Especially variadic templates make things a lot more concise. In the original version I even included return values. You could specify how you process the return values, i.e., collect them all in a vector, or reduce them using some function, like a fold in functional languages. I’ve removed this in this example because, well…, because it’s nasty.

There are some more interesting details about the code (in my humble humble opinion). You can read it here on github. Please don’t hesitate to criticise or comment on this code, I welcome geeky discussions!

Here is the code, for the most up-to-date version check the above-mentioned github link.

/** A flexible implementation of the observer pattern with automatic lifetime
 *  management.
 */
#include <vector>
#include <memory>
#include <functional>
#include <algorithm>

namespace obs
{

// Registration is just a scoped type, once it goes out of scope,
// your registration has expired. The idea is that you store this
// registration as a member of the actual observer, tying it to
// the same lifetime.
using Registration = std::shared_ptr<void>;


// Nothing to see here, the detail namespace is used for just that:
// implementation details 
namespace detail
{
  // We want to create a base class for handling all the observer
  // boilerplate, because of that we need to store 'untyped' pointers.
  using UniversalPtr = std::shared_ptr<void>; 

  // In this implementation we often need to know if an object is alive or not.
  // The way we do that is by using shared_ptr and weak_ptr. Often we do not
  // even care to store anything at all, we're just interested in using the
  // deterministic destructor (RAII). This function creates a 'dangerous'
  // shared_ptr because it points to 'DEADC0DE', this avoids and extra 'new',
  // we could have used make_shared but that does not allow a customer deleter.
  // Note that the deleter should not free any memory since we do not actually
  // allocate anything.
  template <typename Deleter>
  UniversalPtr createEmptyPtr(Deleter deleter)
  {
    return UniversalPtr((void*) 0xDEADC0DE, deleter);
  }
  
  // Slap the baby's bottom. This functions creates a complete no-op shared_ptr
  // which we use as the 'heartbeat' for the Subject
  UniversalPtr createHeartBeat()
  {
    return createEmptyPtr([] (void*) {});
  }

  // A base class for handling common code in all Subjects. Normally using
  // inheritance for code reuse is bad but in this case it is a common idiom
  // to avoid 'code bloat' due to the templating. I.e., if we would leave
  // this code in the actualy template class it would needlessly get replicated
  // for each instantiation with a different type.
  class SubjectBase
  {
  protected:
    SubjectBase(): heartBeat_(createHeartBeat()) {}

    Registration registerObserver(UniversalPtr fptr)
    {
      observers_.push_back(fptr);
      std::weak_ptr<UniversalPtr::element_type> weakHeartBeat(heartBeat_);

      // we pass the function pointer and a weak_ptr into this lambda by
      // value. This is important because it lets the observer know if the
      // subject is still alive at the moment of unregistering.
      // This is basically the Registration, it's a placeholder that removes
      // the observer from the list when the registration goes out of scope.
      return createEmptyPtr([fptr, weakHeartBeat, this] (void*) 
      { 
        if (auto isBeating = weakHeartBeat.lock())
        {
          observers_.erase(std::remove(begin(observers_), end(observers_), fptr), 
                           end(observers_));
        }
      });
    }

    std::vector<UniversalPtr> observers_;

  private:
    UniversalPtr heartBeat_;
  };
 
} // eons detail

// We need this otherwise we cannot explicitly specialise for the funtion
// signature later
template <typename Signature>
struct Subject;

// The Subject class is specialised on the type of 'notification' you
// want to send to the observers. Like Registration the Subject objects
// should be tied to the lifetime of what is the real subject, preferably as
// a member variable.
template <typename Return, typename... Params>
struct Subject<Return (Params...)> : detail::SubjectBase
{
  using F = std::function<Return (Params...)>;
  using FPtr = std::shared_ptr<F>; 

  // instead of the Java-esque 'notify' we just use operator()
  void operator()(Params... params)
  {
    for (const auto& observer : observers_)
    {
      const FPtr fptr = std::static_pointer_cast<F>(observer);
      (*fptr)(params...);
    }
  }

  Registration registerObserver(F f)
  {
    FPtr fptr(new F(std::move(f)));
    return SubjectBase::registerObserver(fptr);
  }

};

} // eons obs

Leave a Reply

Your email address will not be published. Required fields are marked *