The observer1 design pattern is by far the most popular and widely known among behavioural design patterns2. Unfortunately, unlike other mainstream languages out there, the C++ standard library doesn’t provide out of the box observer implementation. Luckily, Boost contains Signals2, a signal/slot3 library which can serve as a basis for an observer. Using Signals2 as it is, however, is not so convenient in object‐oriented program due to the need of manually coded register and notify class methods for each of signal/slot pairs. This article suggests an observable mixin4 which attempts to solve the outlined problem.
Motivating Example
Suppose we are crafting a Window
class for a GUI application:
The Window
class is probably wrapping some third‐party GUI library which is irrelevant for our example. What is relevant, however, is that there exists an Application
class which wants to receive notifications whenever something happens in the Window
:
For the sake of example, let’s say that we are interested in only two events: ShowEvent
and CloseEvent
:
The ShowEvent
is an information only event which is fired whenever the window is shown. On the other hand, the obviously purposed CloseEvent
lets the user cancel the close operation by returning the value false
, but only if the force_close
flag is also false
(otherwise the return value is ignored by the window).
Let us define the two events mentioned along with corresponding register and notify methods with the help of Boost.Signals2 (note, in the simplest cases such as this, e.g. if there are no multiple handlers for a single event, we can use just std::function
instead of boost::signals2::signal
):
The main issue with the above code, as you can see, is that register and notify methods need to be written manually for each of events. And a real‐world window class can easily contain dozens of them! The next section will address this issue by presenting a convenient mixin class which automatically generates the needed methods for you given the list of event handler signatures. Think of wxWidgets static event tables or MFC message maps, but without the use of macros. Or think of Qt signals and slots or Visual C++ event handling, but without the use of compiler extensions.
Implementing Observable Mixin
Here is a UML class diagram which presents a high‐level view on what we will be discussing in this section. We’ll continue to use the window example from the previous section.
The WindowObservers Class
Obviously enough, we can’t just put our signals into homogeneous container like std::vector
because of different event signatures. Fortunately, the C++ standard library provides std::tuple
, an integer‐indexed heterogeneous container which solves our needs (alternatively, we can use a tag‐indexed heterogeneous container, such as boost::fusion::map
). With the help of std::tuple
, we can define an observer table for our Window
class as shown below:
Here, we are making use of enumeration to index observers. This approach is not ideal: an insertion or removal of an observer from the tuple definition may cause a nasty bug if we are not careful enough to adjust the enumeration accordingly. The tag‐based heterogeneous containers are more immune to this issue due to the fact that the tag is mentioned explicitly as part of container element type.
The Observer Class Template
The Observer
type used in the above code fragment is just a simple convenience wrapper for boost::signals2::signal
:
Aside from the signal itself, the type contains a couple of convenience type aliases and a friend declaration for the Observable
class.
The Observable Class Template
The Observable
class is what makes use of our WindowObservers
structure to generate the corresponding registration and notification methods:
The Observable
class maintains a table (std::tuple
in our case) of observers the definition of which is passed as class template parameter Observers
. An example of such an argument is the WindowObervers
structure defined earlier.
The class provides Register
method which is used obviously for observers registration. The function takes an arbitrary callable object (whatever Boost.Signals2 happens to support as a slot type), represented by F&& f
parameter, and an index into the tuple (ObserverId
template parameter). The function returns boost::signals2::connection
object which can be used later to unregister the observer.
The class also has Notify
method which invokes callable objects registered earlier for particular observer kind (which is given by the means of the ObserverId
template argument). The Notify
method forwards its function arguments (args
) to the callable object. The function returns the result of the last slot called, wrapped into boost::optional
(this is the default behaviour of boost::signals2::signal
; see the Boost.Signals2 documentation in case you need an advanced return semantic).
The constructor of the class is made protected because this class is not intended to be used on its own.
The Window Class
Our Window
class now derives from Observable
class template parametrised by WindowsObservers
. This give us a possibility to use Register
and Notify
methods on Window
instances.
The Application Class
Finally, the application class registers the callbacks for WindowObservers::ShowEvent
and WindowObservers::CloseEvent
events:
Putting It All Together
Here is a self‐sufficient test program which puts together the above code snippets:
Conclusion
In this article we have seen how modern C++ features, in particular, variadic templates and perfect forwarding allow us to implement a generic variant of observer pattern without the use of either macros or proprietary compiler extensions.
I have also experimented with alternative implementations, namely, the one based on boost::fusion::map
instead of std::tuple
and the other which uses std::function
instead of boost::signals2::signal
. You can find them in a gist.
Footnotes
-
A software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. The observer pattern is also a key part in the model–view–controller (MVC) architectural pattern. ↑
-
Used to manage relationships, interaction, algorithms and responsibilities between objects. The behavioural pattern focuses on the interaction between the cooperating objects in a manner that the objects are communicating while maintaining loose coupling. ↑
-
A language construct for communication between objects which makes it easy to implement the observer pattern while avoiding boilerplate code. For example GUI widgets can send signals containing event information which can be received by other controls using special functions known as slots. ↑
-
A class that acts as the parent class, containing the desired functionality. A subclass can then inherit or simply reuse this functionality, but without creating a rigid, single ‘is a’ relationship (Wikipedia). ↑