Table of Contents
Observer Template (C++)
License
- Author: Terence J. Grant
- License: MIT License
- Last Update: 2016-07-05
- Donate: Your donations are appreciated!
Download
Before downloading or using this product, make sure you understand and accept the terms of the license.
After downloading, make sure to follow the how to use instructions below; they're worth reading.
- Latest version: Observer Template(C++) version 2016-07-05 on GitHub
Please also take a look at running the example as it contains a quick-start example that demonstrates how to use this pattern.
About
The Observer Template is a set of two C++ template class (tSubject
and tObserver
) that allow you to implement the Observer Pattern using your own custom event classes, on any platform that supports at least the C++98 standard. If you're using a version of C++11 or later, the classes also contain support for the "Move constructor" and "Move assignment operator".
Background
I wrote these classes back in 2012 to scratch an itch for some personal projects; I wanted the ability to send "events" like you might do in ActionScript or what you'd see in Java.awt. I really had "Java" in mind when I was designing this, but I think it ended up looking more like how events look and act in Flash / ActionScript.
I based the design of the two classes directly on the "definition" of the Observer Pattern as detailed in the book Design Patterns: Elements of Reusable Object-Oriented Software by the "Gang of Four". However with that said, there was no code used from the book, and everything for this project was written by me from scratch.
It's taken a little over four years for me to feel like it was ready for a public release. Part of the delay was more or less just "life" and work getting in the way, but I'm glad I was able to finally carve out some time to polish it up for release.
I use these classes consistently throughout my personal projects as well as my client's projects (which is mostly iOS apps nowadays), and in one of those cases an app that's being used by roughly 60,000 people on a daily basis.
The Observer Template is a cornerstone for me, and perhaps you might find it useful too.
Feedback
If you'd like to take a critical look at how the classes look and work, in the repo above, take a look in the directory called "specimen", which contains a "separated" directory, with class definitions and implementations each in a separate file, or "combined", which contains all class definitions and implementations contained in a single header.
If you find any issues with the source, please let me know. I'm open to contributions to the source, as long as it keeps the classes simple and minimalist. I'm open to add example code to the project for tutorial purposes, particularly if the example can run on multiple platforms.
If you think this page can be cleaned up or made simpler, I'm open to that as well, just contact me with suggestions, and if they're appropriate, I'll consider incorporating them.
Finally, if you use this set of classes in your project– link to this page, and I'll link to your page as well. But you'll have to link to me first
How to Use
Setup
- First, copy the header
tObserver.h
into your project - Next, include the header somewhere in your project or precompiled header if you use one
#include "tObserver.h"
Template Classes
You'll have the following template classes available for use in your project:
tSubject<T>
- A class you'll inherit from when you want to send events with event class typeT
tObserver<T>
- A class you'll inherit from when you want to receive events with event class typeT
tSubject
The tSubject<T>
class does the majority of the "heavy lifting" with regards to the actual observer pattern. The class contains the following public
methods:
::attach(tObserver<T>* observer)
- attaches an observer to this subject. Parameterobserver
must be non-null and a unique pointer for the event class type. Parameterobserver
will receive event notifications on the next::notify
call made via this subject.::detach(tObserver<T>* observer)
- detaches an observer from this subject. Parameterobserver
must already be attached to the subject for the event class type.::detachAll()
- convenience method, detaches all existing observers for a particular subject. There are no restrictions on this call.::notify(T message)
- notifies any attached observers for the event class type, in the order the observers were added. Subject must not already be notifying for the same event class type or undefined behavior will occur.::notify
can terminate early if the subject class is destructed during the notification event.
tObserver
The tObserver<T>
class also does some "heavy lifting", but significantly less of it. It does contain one abstract virtual method ::update
, that you must override in your inheriting classes however. The class contains the following public
methods:
::update(T message)
- Pure virtual method you must implement in your inherited classes. It's up to you what you do in this method. For this method to be called, anobserver
must beattach
ed to asubject
, and thesubject
's::notify(T message)
must be called.
Notes on both classes
In both classes, included are a default constructor, copy constructor, and copy assignment operator, which are C++98 compatible, and handle basic initialization and proper copying of the underlying data structures.
For C++11 and later versions, a move constructor and move assignment operator are provided as well.
- If you create a class that inherits from one or more
tSubject<T>
's, and intend to make use of the copy or move constructors / assignment operators, you'll need to ensure that you also provide your own set of copy / move constructors and assignment operators, and ensure that those methods call the underlyingtSubject<T>
versions of each. - Otherwise, if you decide you don't need copy / move support, you're can just ignore them and not worry about it.
The non-public methods you should never call yourself; basically they just ensure the attachments / detachments update correctly.
Suggested Usage
Below, I'll provide some basic theory, usage tips, and precautions. Nothing here is really out of the ordinary, but there's a few edge cases you'll likely to run into that you should be aware of.
Theory
With these template classes, you can create "subjects" and "observers" to send and receieve:
- PODs (plain old data) "types" (like
int
s,double
s,char
s) - Classes (
std::string
,std::vector<std::pair<char, bool> >
,MyEventClass
) - Pointers to anything in the first two categories (like
double *
,std::string *
,MyEventClass *
) - References to anything in the first two categories (
size_t &
,std::string &
,MyEventClass &
) - Even
const
references to anything in the first two categories (const MyEventClass &
)
As it turns out, the very last type– const
references– give us the most flexibility with the least cost, if done correctly.
Benefits of const
references:
const
references for event classes allow us to instantiate temporary objects during::notify
calls::update
calls become "call by reference", which is very light-weight; the object isn't "copied" during the call, and you get the benefits of a "pointer" without having to worry about NULLconst
references mean that the event cannot be modified during a::notify
by any particular::update
, which is typically ideal anyway
The last "benefit" might be a drawback if you're looking to modify an event during a notify– however in that case, you can just use non-const references and you're good to go.
1. Create an "Event Class"
With the theory in mind, let's construct a small example to demonstrate a basic use case, in the style I'd recommend.
We'll start with "Event Classes."
"Event classes" are just normal classes. There's actually no "required" structure to these, but I'll provide you with an example of what I typically do.
Since GUIs tend to be good examples of where the observer pattern might shine, let's create an "Event class" that defines a "Window Event." We won't fully flesh out what you might do if you were really making a "Window Event" class, but we'll provide enough variety to show some flexibility.
Here's what we'll include with the class:
- We'll call it
WindowEvent
as a generic type name - We'll provide an
enum
for the specific event "subtype", to say "what kind" ofWindowEvent
are we sending - We'll encapsulate these events in the enum: open, close, resize, gain focus, lost focus, and minimized
- We'll provide data associated with the above subtypes, with the knowledge that we only send data relevant to an event, for instance: not necessarily giving "focus info" during a "resize" event or vice versa
- We'll provide a pointer to the source of the event– the class that is sending the event, with the option of it being
NULL
just for fun - We won't provide a default constructor– you must explicitly construct the type of event you want
- We will provide a minimal set of convenience constructors that can construct each event type with relevant data
Okay, so with that in mind, your WindowEvent class could look like this:
class WindowEvent { public: enum EventType { kOpened = 0, kClosed, kResized, kFocusChanged, kMinimized, }; public: Window* mSource; EventType mEvent; size_t mWidth, mHeight; bool mHasFocus; public: WindowEvent(const EventType& newEventType, Window* newSource = NULL, bool newHasFocus = false) : mSource(newSource), mEvent(newEventType), mWidth(0), mHeight(0), mHasFocus(newHasFocus) { } WindowEvent(const EventType& newEventType, Window* newSource, const size_t& newWidth, const size_t& newHeight) : mSource(newSource), mEvent(newEventType), mWidth(newWidth), mHeight(newHeight), mHasFocus(false) { } };
Constructing WindowEvents as temporary objects will look something like this:
WindowEvent(WindowEvent::kOpened, window); WindowEvent(WindowEvent::kClosed); WindowEvent(WindowEvent::kResized, window, 128, 96); WindowEvent(WindowEvent::kFocusChanged, true);
Of course you can create events like this too:
WindowEvent myEvent(WindowEvent::kResized, window, 128, 96); WindowEvent myOtherEvent = WindowEvent(WindowEvent::kFocusChanged, true);
But we won't need to do it this way because we'll be using const
references / temporary objects.
2. Create a "Subject" class
Now that we have defined an "Event class", let's define a subject to send it.
Since we want to take advantage of using temporary objects during ::notify
, we'll inherit from tSubject<T>
using a const
reference to WindowEvent
as the T
parameter type.
We'll also include a forward declaration to WindowEvent
in case we haven't defined it yet.
class WindowEvent; // Forward Declaration of event class if necessary class Window : public tSubject<const WindowEvent&> { . . // No additional code necessary specific to tSubject<T> . };
…And at a minimum, that's all you need to do!
Now you can ::notify
using the WindowEvent
class, using temporary objects:
Window myWindow; myWindow.notify(WindowEvent(WindowEvent::kOpened, window)); myWindow.notify(WindowEvent::kClosed); myWindow.notify(WindowEvent(WindowEvent::kResized, window, 128, 96)); myWindow.notify(WindowEvent(WindowEvent::kFocusChanged, true));
In the second notify call above, you'll notice I just passed the enum type; this will still work as the compiler knows ::notify
takes WindowEvent
types, and WindowEvent
types have a constructor that supports a single parameter of WindowEvent::EventType
, so a WindowEvent
will be constructed in this case.
3. Create an "Observer" class
So we have a "Subject" called Window
that can send events in the form of an "Event Class" called WindowEvent
(or really a const WindowEvent &
.)
Now we need a class that can listen for these events, (or rather, "Observe" them.)
So we'll make a class called App
, and make it inherit from tObserver<T>
of type const WindowEvent &
. As a reminder, any class can be a tSubject<T>
or tObserver<T>
, and the choice in this case is purely arbitrary and just for example purposes.
Here's the class definition for App
:
class WindowEvent; // Forward Declaration of event class if necessary class App : public tObserver<const WindowEvent&> { . . . virtual void update(const WindowEvent& message); };
As part of the contract in inheriting from tObserver<T>
, we need to provide a definition of ::update(T)
.
In this case we've also defined it as virtual
, in case we want to later inherit from App
and change how we handle WindowEvent
classes, but otherwise virtual
is completely optional and not required.
When you implement the ::update
method of an observer, it's at this point where you actually want to handle the events you have sent out.
Using the WindowEvent
we've defined earlier, we'll handle each event "subtype", that is the enum EventType
we defined earlier, and do different things depending on what events we can handle.
void App::update(const WindowEvent& message) { switch(message.mEvent) { case WindowEvent::kWindowOpened: printf("*** The window opened!\n"); break; case WindowEvent::kWindowClosed: printf("*** The window closed!\n"); break; case WindowEvent::kWindowResized: printf("*** The window resized to: (%lu, %lu)\n", message.mWidth, message.mHeight); break; case WindowEvent::kFocusChanged: printf("*** The window's focused change and now it... %s\n", (message.mHasFocus) ? "has focus" : "lost focus"); break; default: printf("*** %s\n", "Got a window message I couldn't handle!"); break; } }
And with that, we've defined all the classes and methods we need to implement sending and receiving events. All that's left is to wire the subject and observer classes together, and start actually sending events from subjects to observers.
4. Attaching the "Observer" to the "Subject"
Now that we've fully defined the "Subject class" Window
, and the "Observer class" App
, both are ready to communicate with each other.
So that's just what we'll do– we'll "attach" the "Observer" App
to the "Subject" Window
, via the Subject Window
's ::attach
method:
void someStartupMethod() { App* app = new App(); Window* window = new Window(); window->attach(app); }
And that's all the setup you have to do. Now when you call Window
's ::notify
method, App
will receive ::update
s with whatever events you send.
One important point to note in the code above (and all throughout this example), is that the App
class doesn't have a pointer to Window
and Window
doesn't have a pointer to App
. Now obviously, tSubject<T>
and tObserver<T>
are handling all of this behind the scenes for you, but otherwise the classes know absolutely nothing about each other.
In fact App
and Window
don't need to know anything about each other, no class definitions, includes, or anything like that. All they need to know about is the WindowEvent
type, and any third party can ::attach
them together.
5. Notifying Observers about Events
Finally, let's send some events.
Typically, you'll want your Subject class to "own" the responsibility of sending notifications.
Also, since we're not "really" implementing a Window class with nuances like tracking focus, position, size, etc, the method we'll define will just send events as a test– just for demonstration purposes.
With that in mind, we'll define a method in Window
called ::testNotifications
, that will send one of every type of message, just to prove that App::update
will get called with the correct event type.
void Window::testNotifications() { notify(WindowEvent(WindowEvent::kOpened, window)); notify(WindowEvent::kClosed); notify(WindowEvent(WindowEvent::kResized, window, 128, 96)); notify(WindowEvent(WindowEvent::kFocusChanged, true)); notify(WindowEvent(WindowEvent::kFocusChanged, false)); notify(WindowEvent::kMinimized); }
Now, if you were to run the code in this example, you'd see the following output:
*** The window opened! *** The window closed! *** The window resized to: (128, 96) *** The window's focused change and now it... has focus *** The window's focused change and now it... lost focus *** Got a window message I couldn't handle!
That's it– pretty cool, huh? If you were able to follow that, you should be able to implement tSubject
and tObserver
into your own classes and start sending your own events.
One Very Important Precaution
Okay– we've gone through an example, everything looks great, and I hope you're inspired so far. However, there's one really crazy scenario that can happen– your Subject class's destructor can be called during a ::notify
call. And it's totally normal, and you should expect it. I think the best way to display this is with a simple visual, so here we go:
Okay, so let's break graphic down:
Window
ownsButton
- Creates
Button
during the constructor delete
sButton
during the destructorButton
sends aButtonEvent::kButtonPressed
- We'll assume the
WindowManager::close
then handles by deleting theWindow
instance immediately - Which in turn deletes the
Button
instance.
So if you do all of the "math" behind the call stack, it turns out that in this particular case, Button::notify
ends up eventually calling Button::~Button
during Button::sendButtonPressed
!
What this means in this case– and in general– be aware that your class's context (this
) may be gone after a ::notify
completes. So this
, your members, and methods are no longer valid to call or access within this after the ::notify
.
However, there are two things that are still valid to do:
- Exiting the method without doing anything else
- Accessing local variables previously defined in the method
So there's two ways to deal with this potential situation:
- Option 1: Make
::notify
the last thing you call in a method
void sendButtonPressed() { . . // Everything important we want to do happens here . notify(ButtonEvent(ButtonEvent::kButtonPressed, this)); // Last thing we do in this method and we're good to go }
This option is fine, and the preferred way if some outside class calls buttonInstance→sendButtonPressed()
, but won't be sufficient if you're calling it via this→sendButtonPressed()
.
So if calling ::notify
last works for you, which it will in a good percentage of cases, you're good to go. Otherwise, there's another alternative.
- Option 2: Dedicate a local variable to "detect" if the destructor has been called, and read it after your
::notify
completes
This requires a little bit of setup, but the idea here is simple. We keep an extra bool
pointer as a member, keep it NULL until we suspect we're going to "delete ourselves", then set the pointer to a local bool
, and check the result after our "dangerous" ::notify
call.
If we detect we're not destroyed, we "reset" the bool
pointer to NULL, and continue executing code. If not– we exit the method right away as our context is completely gone.
Let's make some changes to the Button
class displayed in the above graphic with the idea that we might want to do more after we call ::notify
, but we also know we might trigger our own demise during that ::notify
:
class Button : tSubject<const ButtonEvent&> { protected: bool* mDeletedPtr; public: Button() : mDeletedPtr(NULL) { . . . } ~Button() { if (mDeletedPtr) { *mDeletedPtr = true; } } . . . void sendButtonPressed() { bool wereWeDeleted = false; mDeletedPtr = &wereWeDeleted; notify(ButtonEvent(ButtonEvent::kButtonPressed, this)); if (!wereWeDeleted) { mDeletedPtr = NULL; . . // Hey, we weren't deleted, we can do more stuff! . } } };
This technique is also the same technique tSubject
uses internally to detect if tSubject
itself gets destroyed during a notify so it knows not to access it's members anymore either.
So the key player here is the mDeletedPtr
member, and basically all we've done is add some boilerplate code to initialize it and set the data it points to.
This solution is more necessary in a language like C++ because in the majority of cases you're doing your own memory management. If this were a garbage collected language like Java– it wouldn't be necessary as the garbage collector would only destroy a class when all "references" to it are gone.
Advanced Usage
Well, I don't know if this is "advanced" really, but you may or may not be experienced in the following, so here's some tips and tricks.
Literally, everything below are just nuances and / or limitations of C++, so keep in mind these are not my rules, but just the "fringes" of the language.
Multiple inheritance
As you work more and more with tSubject and tObserver, it will likely make sense for a class to send multiple types of Event Classes, or to listen for multiple types of Event Classes.
Don't worry, nothing will "collide" and everything works independently of each other.
To inherit, simply inherit like you normally would:
class MyClass : public tObserver<const FooEvent&>, public tObserver<const BarEvent&>, public tSubject<const ZooEvent&> { . . . void update(const FooEvent& message); void update(const BarEvent& message); };
The above example can ::notify
the ZooEvent
type, and receive the FooEvent
and BarEvent
types.
Just remember to define your ::update
methods before compiling, or your compiler will likely give you "method shadowing" errors.
Inheriting from a custom Observer
If you already have a class that defines any type of tObserver inheritance, and you want a child class that handles a different type of tObserver inheritance, it can be done, but you'll also have to re-define whatever your parent's handler is, or else you'll likely get a "method shadowing" error from your compiler.
class RemoteHandler : public tObserver<const URLLoaderEvent&> { virtual void update(const URLLoaderEvent& message); }; class GeoIPHandler : public RemoteHandler, public tObserver<const GeoIPEvent&> { virtual void update(const URLLoaderEvent& message) { RemoteHandler::update(message); // Pass to parent class } void update(const GeoIPEvent&); };
In this case, ensure your parent's ::update
method is virtual
, and at a minimum, you can just pass the parameter to the parent's version of ::update
, and you're good to go. Also, your child class can take the opportunity to do their own handling of the same message the parent does– so there's an upside to the extra code required here.
Inheriting from two Subjects
By far the most interesting side-effect of inheriting from two instantiations of the same template is method naming collisions.
class MyClass : public tSubject<const FooEvent&>, //Assume FooEvent has an std::string constructor public tSubject<const BarEvent&> //Assume BarEvent has an std::string constructor { . . . }; void sendMessages() { MyClass* instance = ...; instance->notify("foo event"); // Error! Which notify do we call?! instance->notify("bar event"); // Error! Which notify do we call?! }
If you've dealt with this before, the solution is quite simple– but ugly– but still simple.
void sendMessages() { MyClass* instance = ...; instance->tSubject<const FooEvent&>::notify("foo event"); // OK! Call the FooEvent constructor and notify instance->tSubject<const BarEvent&>::notify("bar event"); // OK! Call the BarEvent constructor and notify }
Again– this is just how C++ deals with multiple inheritance when naming collisions happen. Not my invention!
Disabling Assertions
If you've built an app, pressure tested it, and ready to release it to the public, I highly suggest you disable assertions; you might see significant speedups. When assertions are on, checks are done during tSubject
's ::attach
and ::detach
methods to ensure observers have proper membership / non-membership in the notification list. With them off, checks aren't made– but if your logic is solid, you won't need the checks anyway.
You do this by either passing -DNDEBUG
as a command-line option to your compiler, or by putting this line at the top of any file that includes tObserver.h
:
#define NDEBUG
Running the unit tests
The Observer Template comes with two unit test class, tSubjectTests.cc
and tObserverTests.cc
, and three project files:
/project/win/ObserverTemplateTests.sln
- Visual Studio 2005, Windows console target/project/mac/ObserverTemplateTests.xcodeproject
- Xcode 7 Mac OS console target/project/ios/ObserverTemplateTests.xcodeproject
- Xcode 7 iOS console target
When running the unit test on Visual Studio, note you will first need to convert the solution to the latest version. You'll also need to put a breakpoint on the last line of the main
function, as the console window containing the results of the test will close on exit.
When running the unit test on Mac or for iOS, the results of the test will be printed in Xcode's debug console, which is at the bottom right of Xcode's window.
Also, when running for iOS, you'll note that an app won't appear or launch in the simulator or on a device, as the tests are purely meant to display in the debug console.
(If you're curious, these project files were generated with Google GYP; the source project file is located in /project/common/ObserverTemplateTests.gyp
.)
Running the example
I've included the "WindowEvent" example into the repo, which is based on the sample code from above.
/project/win/WindowEvent.sln
- Visual Studio 2005, Windows console target/project/mac/WindowEvent.xcodeproject
- Xcode 7 Mac OS console target/project/ios/WindowEvent.xcodeproject
- Xcode 7 iOS console target
As stated in the example code, this is just a light demonstration of the code and does not implement anything related to platform-specific "windowing."
When running the example on Visual Studio, note you will first need to convert the solution to the latest version. You'll also need to put a breakpoint on the last line of the main
function, as the console window containing the results of the test will close on exit.
When running the unit test on Mac or for iOS, the results of the test will be printed in Xcode's debug console, which is at the bottom right of Xcode's window.
Also, when running for iOS, you'll note that an app won't appear or launch in the simulator or on a device, as the tests are purely meant to display in the debug console.
(If you're curious, these project files were generated with Google GYP; the source project file is located in /project/common/WindowEvent.gyp
.)
Q & A
If you have a question not answered here, please feel free to contact me and ask.
vs Boost?
- Q: What makes this better or different than Boost.Signals or Boost.Signals2 or the many "Signals and Slots" libraries?
- A: Boost's Signals and the various "Signals and Slots" libraries are just one approach to solving the Observer Pattern.
What I'm proposing instead is a method to use the Observer Pattern directly, which requires one header containing two classes, and almost no learning curve.
That said, I've used boost (in general) before on a day-to-day basis for client projects. I'm not a boost "hater", but I'm not a regular user of it either.
Thread safety?
- Q: Is this thread safe?
- A: Yes– but treat it as if you were dealing with the STL directly and you'll be fine.
Underneath the hood both classes use STL's std::list
type.
Anytime you call either class's constructor or destructor, tSubject
's ::attach
, ::detach
, ::detachAll
, or ::notify
, or tObserver
's ::update
, you're potentially writing (yes, *writing*, in each case) to an std::list
.
As long as you plan your thread-aware code properly with that in mind, you should have no problems.
History
July 5, 2016
- Added the "WindowEvent" example to the repo as a runnable demo
- Added GitHub link to download section
- First public release
- Created project page
June 28, 2016
- Preparing for public release
April 1, 2012
- Initial discovery of "deleted while notifying" and discovery of solutions to this issue
January 1, 2012
- Initial development