Another classic Gang of Four Pattern is the Observer Pattern. In this pattern, observers want to be notified about state changes of a subject. In this post we will look how to easily implement this with std::function.

As before I will stick to a sensor example. Let’s assume we have a sensor that changes its state in unpredictable intervals and different parts of your system need to know about these changes. Of course you could pull the sensor state form each part. However this is not very elegant and might lead to several unneeded busy loops. In the classic pattern the subject would keep a list of the observers but since C++11 we can register callbacks very easily with std::function. For simplicity we will assume the sensor state is represented by an int.

Low effort observers

#include <functional>
#include <list>

class Sensor {

std::list<std::function<void(int)>> callbacks_{};

public:

void attach(std::function<void(int)> callback) {
  callbacks_.emplace_back(callback);
}

void measure() {
  // some complex measurment logic which is waiting for hardware
  // state changes...
  const int new_sensor_state = 42;
  notify(new_sensor_state);
}

private:

void notify(int state) {
  for (const auto& callback : callbacks_) {
      callback(state);
  }
}

};

The usage would then be like this:

Sensor sensor;
sensor.attach([](int state) {
  std::cout << "New sensor state: " << state << std::endl;
});

That’s it. Super simple and implemented within minutes. Most interestingly there is no observer class in this observer pattern.

But you might have realized that there is one small catch. There is no detach method. Once we registered an observer with attach there is no way to deregister. That is due to the fact, that std::function is not comparable (or only against nullptr). In many cases this is fine and you want to observer a state during the whole runtime. However if you need to be able to unregister there is an easy fix for this.

Observers with handles

When attaching a callback to our sensor we can just return a handle. When we then want to unregister from notifications about the state changes of the sensor we pass this handle to the detach method.

Change the Sensor::attach method to return a handle to the inserted callback:


std::list<std::function<void(int)>>::iterator
attach(std::function<void(int)> callback) {
  callbacks_.emplace_back(callback);
  return --callbacks_.end();
}

And add a Sensor::detach method:


void detach(std::list<std::function<void(int)>>::iterator handle){
  callbacks_.erase(handle);
}

And use it like this:

Sensor sensor;
auto handle = sensor.attach([](int state) {
  std::cout << "New sensor state: " << state << std::endl;
});

/// Do some stuff...

sensor.detach(handle);

Unfortunately there is no free lunch. We now placed a burden on the user to keep track of the handles. But as long as std::function is not comparable there is no easy workaround for this. If keeping track of handles is not acceptable you might at this point be better off not reinventing the wheel and use an existing library e.g. boost::signals2 or Qt signals

References