Observer Template (C++)

License

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 type T

tObserver<T> - A class you'll inherit from when you want to receive events with event class type T

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. Parameter observer must be non-null and a unique pointer for the event class type. Parameter observer will receive event notifications on the next ::notify call made via this subject.

::detach(tObserver<T>* observer) - detaches an observer from this subject. Parameter observer 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, an observer must be attach ed to a subject , and the subject '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 underlying tSubject<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 NULL const 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" of WindowEvent 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!

" ) ; break ; case WindowEvent :: kWindowClosed : printf ( "*** The window closed!

" ) ; break ; case WindowEvent :: kWindowResized : printf ( "*** The window resized to: (%lu, %lu)

" , message. mWidth , message. mHeight ) ; break ; case WindowEvent :: kFocusChanged : printf ( "*** The window's focused change and now it... %s

" , ( message. mHasFocus ) ? "has focus" : "lost focus" ) ; break ; default : printf ( "*** %s

" , "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 owns Button Creates Button during the constructor delete s Button during the destructor Button sends a ButtonEvent::kButtonPressed We'll assume the WindowManager::close then handles by deleting the Window 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 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