Magazine > Issues > 2006 > March >  C++ at Work: Event Programming, Part 2
C++ At Work
Event Programming, Part 2
Paul DiLascia

Code download available at: CAtWork0603.exe (3,178 KB)
Browse the Code Online

Last month I answered a question about implementing native events in C++ (see C++ At Work: Event Programming). I discussed events in general and showed how to use an interface to define event handlers for your class that clients must implement to handle events. My implementation had some shortcomings that I promised to resolve in the sequel, so here I am to finish the story.
Figure 1 Calculating Prime Numbers 
But first, a brief review of the previous episode. I wrote a program, PrimeCalc (Figure 1), with a class CPrimeCalculator that calculates prime numbers. CPrimeCalculator raises two events: Progress and Done. As it searches for prime numbers, CPrimeCalculator raises the Progress event to report how many primes it's found so far. When it's finished, it raises the Done event. The events are defined by an interface, IPrimeEvents:
class IPrimeEvents {
public:
  virtual void OnProgress(UINT nPrimes) = 0;
  virtual void OnDone() = 0;
};
Clients that want to handle prime events must derive from IPrimeEvents, implement the handler functions, and call CPrimeCalculator::Register to register their interface. CPrimeCalculator::Register adds the client object/interface to its internal list. When it's time to raise a Progress event, CPrimeCalculator calls a helper function NotifyProgress:
void CPrimeCalculator::NotifyProgress(UINT nFound)
{
  list<IPrimeEvents*>::iterator it;
  for (it=m_clients.begin(); it!=m_clients.end(); it++) {
    (*it)->OnProgress(nFound);
  }
}
NotifyProgress walks the client list, calling each client's OnProgress handler. As a programmer using CPrimeCalculator, writing the code to handle events is straightforward and easy—just derive from IPrimeEvents and implement the handlers. But as a programmer implementing a class like CPrimeCalculator that raises events, the mechanism is tedious. You have to implement a NotifyFoo function for every Foo event, even though each one has the same familiar pattern. And the event-raising code is split across two classes, the event interface (IPrimeEvents) and the event source class (CPrimeCalculator). What if you want to use the same event interface with different event sources? IPrimeEvents is pretty generic. I might rename it IProgressEvents and use it with any worker class that reports Progress in the form of an integer and Done when it's finished. But each class that raises progress events has to (re)implement the notification functions that raise the event. Ideally, all the event code should reside in a single class.
Since the notification functions follow a predictable pattern, it's natural to ask: Isn't there some way to implement them generically? Shouldn't I be able to encapsulate the entire event mechanism in a single class or template or macro or something any event source can use? The answers are yes and yes. I'll show you how to create an event system that uses macros and templates to reduce event coding to the barest minimum. Our journey requires flying high into the C++ stratosphere of nested templates and functor classes.
I'll implement the system in several steps. The goal is to write a template that implements the notification functions NotifyProgress and NotifyDone. Each function has a similar but not identical pattern:
// NotifyFoo — raise Foo event
list<IPrimeEvents*>::iterator it;
for (it=m_clients.begin(); it!=m_clients.end(); it++) {
  (*it)->OnFoo(/*args*/);
}
That is, iterate the client list, and call OnFoo for each client, passing the event parameters. How can I write this as a template? I can parameterize the interface IPrimeEvents as a type T, but how do I parameterize the event handler functions OnFoo, which can have any name and signature a programmer chooses?
Whenever you need to parameterize a function, you should think: function class. Also known as a functor, a function class is a C++ sleight-of-hand trick that converts a function to a class, so instead of passing a pointer to a callback function, you pass an instance of the function class. Functors abound in the Standard Template Library (STL), which implements several algorithms that use functors. In particular, the for_each algorithm is useful here:
for_each(m_clients.begin(), m_clients.end(),
  NotifyProgress(nFound));
The for_each algorithm iterates the container from beginning to end and invokes the function object NotifyProgress for each element. But what exactly is this "function object"? It's not a function; it's an object. The class looks like this:
class NotifyProgress {
protected:
  UINT m_nFound;
public:
  NotifyProgress(UINT n) : nFound(n) { }
  void operator()(IPrimeEvents* obj)
  {
    obj->OnProgress(nFound);
  }
};
NotifyProgress implements the function operator()(IPrimeEvents*), which is what the for_each algorithm needs. In general, if you have a collection of objects of type T, the for_each algorithm expects a functor that implements operator()(T). It calls your operator with each T object in the collection. So in this case the function operator takes a pointer to IPrimeEvents and returns void—because the client list is a list of pointers-to-IPrimeEvents. To pass additional parameters, the constructor stores them as data members. Calling NotifyProgress(nFound) is calling the constructor to create a stack instance initialized with m_nFound=nFound. So the general pattern for any Foo functor that raises the Foo event is:
class NotifyFoo {
protected:
  ARG1 m_arg1; // whatever, as many as needed
public:
  NotifyProgress(ARG1 a1, ...) : m_arg1(a1) { }
  void operator()(IMyEvents* obj)
  {
    obj->OnFoo(m_arg1, ...);
  }
};
The constructor saves the event parameters as data members and the function operator passes them to the object's event handler function. The upshot of all this—and of all functors in general—is that I've converted a function OnFoo into a class NotifyFoo. And why is that useful? Because now I can write a template. But before I do that, there's one little thing I want to mention. To earn your gold star, you really should derive your functor from an STL class called unary_function:
class NotifyProgress : 
  public unary_function<IPrimeEvents*, void> 
{
  .
  . // as before
  .
};
This says that NotifyProgress is a unary function whose function operator takes one argument, a pointer-to-IPrimeEvents, and returns void. The unary_function makes your functor class "adaptable," which lets you combine it with STL adapters like not1, bind2nd, and so on. But even if, as is the case with my event handlers, you never plan to use adapters, unary_function is still a good idea because it announces to the world, "this is a functor." It's a way of documenting your code. For a full discussion of adapters, see Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library (Addison-Wesley, 2001) by Scott Meyers.
STL gurus may be wondering why I don't use the mem_fun adapter to convert IPrimeEvents::OnProgress directly to a function object. It's because OnProgress is virtual and you can't adapt a virtual function. If you do, it'll get sliced to the base class. If you're using the Boost library, you could use its bind adapter to directly convert virtual event handlers like OnFoo to functors without writing a functor. If you have no idea what I'm talking about, don't fret, just keep reading.
Of course, I also need a NotifyDone for the Done event. Since the Done event has no parameters, the constructor has none either:
class NotifyDone :
  public unary_function<IPrimeEvents*, void> 
{
public:
  NotifyDone() { }
  void operator()(IPrimeEvents* obj)
  {
    obj->OnDone();
  }
};
So now that I have my functor classes I can use for_each instead of manually iterating the client list. Where should I put them? The functors belong with the event specification, so I'll put them inside IPrimeEvents, as nested classes. Figure 2 shows the code. Astute readers will notice I've made a couple of other minor modifications as well. Instead of naming the functor NotifyProgress, I called it Progress. You'll see how this makes the code more readable later. And instead of declaring all my event handlers pure, I've defined them with empty implementations. IPrimeEvents has only two events, but for a general event mechanism, it seems unfriendly to make programmers implement every event handler when they may be interested in handling only a few. So now each handler gets a default do-nothing implementation. To make the base class abstract, I've declared the destructor pure. This is a standard C++ trick when you want to make a class abstract without having any pure virtual functions. The only catch is you have to define the dtor. A pure virtual function has no definition—unless it's the destructor. Since each derived class dtor calls its base class dtor, the base class needs an implementation even though it's pure:
inline IPrimeEvents::~IPrimeEvents() { }
Now with my functors defined, CPrimeCalculator raises the Progress event, like so:
// in CPrimeCalculator:
void NotifyProgress(UINT nFound)
{
  for_each(m_clients.begin(), m_clients.end(),
    IPrimeEvents::Progress(nFound));
}
/////////////////
// Interface for prime events. This class defines the signatures
// of the event handler functions as well as nested functor 
// classes to use with for_each. Clients that handle prime events must 
// derive from this and override the event handlers for events they 
// handle. 
//
class IPrimeEvents {
protected:
    IPrimeEvents() { }
    virtual ~IPrimeEvents() = 0;

public:
    // Virtual event handler fns. Override the ones you handle.
    virtual void OnProgress(UINT /* nPrimes */) { }
    virtual void OnDone() { }

    // Nested functor class for Progress event. Note how the ctor requires
    // the same parameters as the event handler and stores them in the 
    // object.
    class Progress : public unary_function<IPrimeEvents*, void> {
    protected:
        UINT nFound;
    public:
        Progress(UINT n) : nFound(n) { }
        void operator()(IPrimeEvents* obj)
        {
            obj->OnProgress(nFound);
        }
    };
    
    // Nested functor class for Done event. This time there are no event 
    // args.
    class Done : public unary_function<IPrimeEvents*, void> {
    public:
        Done() { }
        void operator()(IPrimeEvents* obj)
        {
            obj->OnDone();
        }
    };
};

inline IPrimeEvents::~IPrimeEvents() { }

// List (vector) of prime numbers.
typedef vector<UINT> PRIMELIST;

//////////////////////////////////////////////////////////////////
// Class to calculate 1st n prime numbers.
//
class CPrimeCalculator
{
protected:
    list<IPrimeEvents*> m_clients;     // list of client objects to notify
    PRIMELIST m_primes;            // list (vector) of primes numbers found

    // Raise events. Use for_each with function class.
    void NotifyProgress(UINT nFound)
    {
        for_each(m_clients.begin(), m_clients.end(),
            IPrimeEvents::Progress(nFound));
    }

    void NotifyDone()
    {
        for_each(m_clients.begin(), m_clients.end(),
            IPrimeEvents::Done() );
    }

    ...
};
So far, I've introduced functor classes Progress and Done, and now NotifyProgress and NotifyDone can use STL's for_each algorithm. What next? Remember, my goal is to get rid of the NotifyFoo functions entirely—or rather, implement them as templates so programmers creating events don't have to write these boilerplate functions for every event they define. Converting the for loop to a for_each algorithm is only the first step.
The functors set the stage for templates by converting the virtual OnFoo member function to a Foo functor type. (Functors are a bit like .NET delegates in this respect.) Now that my notification functions vary by type instead of function names, I can parameterize them. And when I do that, I can move the whole event implementation out of the source class CPrimeCalculator and into a new template class CEventMgr that's totally generic. Figure 3 shows the result. CEventMgr<I> holds a list of I* pointers. It has Register and Unregister methods to add and remove clients from its list, and it has a template member function Raise that raises an event:
template <typename I>
class CEventMgr
{
  ...
  template <typename F>
  void Raise(F fn)
  {
    for_each(m_clients.begin(), m_clients.end(), fn);
  }
};
////////////////////////////////////////////////////////////////
// Generic event manager for native C++.
//
#pragma once

#include <list>
#include <algorithm>
using namespace std;

//////////////////
// Generic event manager. Holds list of client objects. Template class
// parameterized by the event interface.
//
// - Instantiate in event source class.
//
// - Source calls Raise to raise an event, passing functor object 
// initialized with event params.
//
// - Clients derive from event interface and override event handlers. 
// Clients call Register/Unregister to add/remove themselves.
//
template <typename I>
class CEventMgr
{
protected:
    list<I*> m_clients; // list of registered client objects
public:
    CEventMgr() { }
    ~CEventMgr() { }

    // Register: Add client to list.
    void Register(I* client)
    {
        m_clients.push_back(client);
    }

    // Unregiser: Remove client from list.
    void Unregister(I* client)
    {
        m_clients.remove(client);
    }

    // Nested template member function! This fn calls the function object 
    // F for each registered client. It merely passes F to for_each. Use 
    // the DEFINE_EVENT macros to generate the functors. See IPrimeEvents 
    // in Prime.h for example. 
    template <typename F>
    void Raise(F fn)
    {
        for_each(m_clients.begin(), m_clients.end(), fn);
    }
};

// Macro to declare an event interface. Generates declarations for ctor/dtor.
#define DECLARE_EVENTS(name)                                            \
protected:                                                              \
    name() { }                                                          \
    virtual ~name() = 0;                                                \

// Macro to implement event interface. Defines dtor
#define IMPLEMENT_EVENTS(name)                                          \
    inline name::~name() { }                                            \

// Macros to define events that use 0, 1 or 2 parameters. If you need more
// than two, you should define a struct/class and pass a pointer to it.

// Event with no args: Declare OnFoo handler and Foo functor.
#define DEFINE_EVENT0(iface,name)                                 \
virtual void On##name() { };                                      \
class name : public unary_function<iface*, void> {                \
public:                                                           \
    name() { }                                                    \
    void operator()(iface* obj)                                   \
    {                                                             \
        obj->On##name();                                          \
    }                                                             \
};                                                                \

// Event with one arg: Declare OnFoo handler and Foo functor.
#define DEFINE_EVENT1(iface,name,T1)                              \
virtual void On##name(T1) { }                                     \
class name : public unary_function<iface*, void> {                \
protected:                                                        \
    T1 m_arg1;                                                    \
public:                                                           \
    name(T1 a1) : m_arg1(a1) { }                                  \
    void operator()(iface* obj)                                   \
    {                                                             \
        obj->On##name(m_arg1);                                    \
    }                                                             \
};                                                                \

// Event with two args: Declare OnFoo handler and Foo functor.
#define DEFINE_EVENT2(iface,name,T1,T2)                           \
virtual void On##name(T1, T2) { }                                 \
class name : public unary_function<iface*, void> {                \
protected:                                                        \
    T1 m_arg1;                                                    \
    T2 m_arg2;                                                    \
public:                                                           \
    name(T1 a1, T2 a2) : m_arg1(a1), m_arg2(a2) { }               \
    void operator()(iface* obj)                                   \
    {                                                             \
        obj->On##name(m_arg1, m_arg2);                            \
    }                                                             \
};                                                                \
Holy cow, Batman! How often do you get to write a template-within-a-template? But that's just what's required here. Now to raise an event I can write:
void NotifyProgress(UINT nFound)
{
  m_eventmgr.Raise(IPrimeEvents::Progress(nFound));
}
That's more like it! No for loop, not even a for_each. All the details are encapsulated in CEventMgr, and raising an event is one line of code that knows nothing about the mechanics. I could even dispense with NotifyProgress entirely and simply call CEventMgr::Raise when I want to raise an event—but good coding ethics prevent me because I'd rather encapsulate Raise in a function in case I ever change CEventMgr or expose the event-raising function to clients. Since NotifyProgress is inline, there's no performance lost.
In case templates make your brain hurt, let me spell out what's going on. CEventMgr is a template class parameterized by the event interface I. So CEventMgr<IPrimeEvents> instantiates an event manager based on IPrimeEvents. It holds a data member m_clients, which is a list of IPrimeEvents pointers, list<IPrimeEvents*>. Within CEventMgr is a template member function, Raise<F>, which passes its functor argument F to for_each. So when you write
m_eventmgr.Raise(IPrimeEvents::Progress(nFound));
the compiler sees you're trying to call CEventMgr::Raise with an argument of type IPrimeEvents::Progress, so it uses the template to generate the member function CEventMgr::Raise(IPrimeEvents::Progress). The implementation passes the functor instance to for_each, which invokes the functor's function operator() for each I* object in the client list. The functor calls the object's OnProgress handler—just what I wanted! Aren't templates cool?
We've reached third base, we're almost home. The functors let me parameterize the event methods and use for_each, but they're still several lines long and I so hate typing. So the final step is to introduce some macros to spare my carpal tunnels. Here's the final condensed definition for IPrimeEvents:
class IPrimeEvents {
  DECLARE_EVENTS(IPrimeEvents);
public:
  DEFINE_EVENT1(IPrimeEvents, Progress, UINT /*nFound*/);
  DEFINE_EVENT0(IPrimeEvents, Done);
};
IMPLEMENT_EVENTS(IPrimeEvents);
Figure 4 shows the full code—you can see I'd be hard pressed to condense it any further. Only the essential information appears: the names and signatures for each event handler. The macros assume the name of the Foo event handler is OnFoo. Some programming purists hate macros, but not me. Why not use every tool at your disposal? DECLARE_EVENTS declares the constructor and destructor; IMPLEMENT_EVENTS implements the inline dtor. The macros DEFINE_EVENT0, DEFINE_EVENT1, and DEFINE_EVENT2 declare/define the OnFoo event handlers and Foo functors for events with zero, one, or two parameters. If you need more than two parameters, you can define a struct and pass a pointer to it as a single event parameter:
MumbleArgs args;
args.a = 1;
args.b = 2;
// etc.
m_eventMgr.Raise(IMyEvents::Mumble(&args));
// Now the whole thing is defined with macros.
//
class IPrimeEvents {
    DECLARE_EVENTS(IPrimeEvents);
public:
    DEFINE_EVENT1(IPrimeEvents, Progress, UINT /*nFound*/);
    DEFINE_EVENT0(IPrimeEvents, Done);
};

IMPLEMENT_EVENTS(IPrimeEvents);

// List (vector) of prime numbers.
typedef vector<UINT> PRIMELIST;

//////////////////////////////////////////////////////////////////
// Class to calculate 1st n prime numbers.
//
class CPrimeCalculator
{
protected:
    CEventMgr<IPrimeEvents> m_eventmgr;     // instantiate general event manager
    PRIMELIST m_primes;         // list (vector) of primes numbers found

    ...

    // Raise events. The notification functions simply call
    // CEventMgr::Raise with the appropriate functor object.
    void NotifyProgress(UINT nFound)
    {
        m_eventmgr.Raise(IPrimeEvents::Progress(nFound));
    }

    // Notify clients I'm Done.
    void NotifyDone()
    {
        m_eventmgr.Raise(IPrimeEvents::Done());
    }

public:

    // Register client to receive events
    void Register(IPrimeEvents* client)
    {
        m_eventmgr.Register(client);
    }

    // Unregister client from receiving events.
    void Unregister(IPrimeEvents* client)
    {
        m_eventmgr.Unregister(client);
    }
};
Alternatively, you could implement DEFINE_EVENT3. But remember: functor objects are passed by value, so they should be small. Why copy a ton of parameters back and forth across the heap and stack when you can pass a pointer instead? You can also use a struct if your event handlers need to return something. I made the handler functions return void in order to simplify my life.
Programmers sometimes ask if functors introduce a lot of extra overhead. In fact, functors are usually more efficient than functions. The reason is inlining. When you pass a pointer-to-function in C++, it really is a pointer, even if you define your function inline. You can't pass a function by value. But when you pass an object instance to a template function, the compiler can generate everything inline if you define your functions that way. In the case of events, there's still only one function call through a pointer, which happens when the function operator invokes the virtual OnFoo handler.
Happy programming!

Send your questions and comments for Paul to  cppqa@microsoft.com.


Paul DiLascia is a freelance software consultant and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). In his spare time, Paul develops PixieLib, an MFC class library available from his Web site, www.dilascia.com.

Page view tracker