The Artima Developer Community
Sponsored Link

Weblogs Forum
Event Driven Programming using Template Specializations in C++

12 replies on 1 page. Most recent reply: Dec 21, 2004 11:59 AM by

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 12 replies on 1 page
Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Event Driven Programming using Template Specializations in C++ (View in Weblogs)
Posted: Dec 18, 2004 8:48 AM
Reply to this message Reply
Summary
C++ template specializations provide an elegant mechanism for implementing event-driven frameworks. This is the technique that I am using for defining semantic actions in the YARD parser. This is a reprint of an article I just posted to CodeProject.com.
Advertisement

Introduction

Template specializations provide a mechanism by which we can create an event-driven framework, instead of using function pointers.

Template specializations are alternative definitions of a template for specific parameter values. They are sometimes known as user-defined specializations. For more information I recommend reading The C++ Programming Language, 3rd Edition by Bjarne Stroustrup.

There are two advantages of this approach, first no function pointer registration step is needed like in typical event-driven frameworks, and secondly the compiler can do a far better job of optimizing the call. 

Event-Driven Programming

Event-driven code is useful when writing a library and we want the library to provide default behaviour in response to certain events, but to allow library users to provide custom behaviour to one or more events. This occurs frequently in the implementation of GUI libraries. A message loop has to dispatch events to user-defined functions. This is typically done either through virtual-functions or through function-pointers. Using template specializations is an easy to use and efficient alternative.

The Code

Here is a sample program, which demonstrates the usage of template specializations for callbacks:

  const int RED_PILL = 0;
  const int BLUE_PILL = 1; 

  template<int T>
  struct EventHandler {
    static void Event() { };
  }; 

  template<typename Dummy_T = void>
  struct Dispatcher {
    static bool EventDispatch(char ch) {
      switch (ch) {
        case 'a': {
          EventHandler<RED_PILL>::Event();
          return true;
        }
        case 'b': {
          EventHandler<BLUE_PILL>::Event();
          return true;
        }
        default : {
          return false;
        }
      }
    }
  }; 

  template<>
  struct EventHandler<BLUE_PILL> {
    static void Event() { puts("Welcome to the matrix!"); };
  }; 

  int main() {
    puts("press a for the red-pill, b for the blue-pill");
    char ch = getchar();
    Dispatcher<>::EventDispatch(ch);
    return 0;
  }

Explanation

This code represents how an event-driven library could be created. The basic EventHandler and Dispatcher classes represent what would be found in the library. The specializations and the main() function represent what would be defined by the user of the library,

The first EventHandler class is a template which contains empty functions defintions. This is the class which is specialized. The other EventHandler class is are the template specializations defined by the user of a library. The programmer simply needs to provide implementations of the Event() function and can do whatever else they want inside of it.

The Dispatcher class houses a Dispatch() function which triggers the appropriate user-defined event. The template parameter is ignored, but is provided to make sure that the class is constructed by the compiler after the specializations are defined by the programmer. 

Notice that there is no explicit specialization EventHandler<RED_PILL>. The default handler is called in this case, which does nothing. The call should be entirely removed by the optimizer.  

Why is EventDispatch inside of a template?

This is a drawback of the technique due to compilation order dependencies. Typically in an event-driven framework we will want to define the specializations in code after the code where the specialization is used. As soon as the compiler sees a usage of the template it is too late for us to define a specialization. What I do then is define the usage within a template. This allows me to put off compilation of the usage until after the definition of the specializations.


Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 18, 2004 12:47 PM
Reply to this message Reply
Seems like the code example should use RED_PILL and BLUE_PILL. Typo?

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 19, 2004 8:36 AM
Reply to this message Reply
Thank you for pointing that out Michael! The Artima.com Blog entry editor has problems with < >.

I modified the code and article at the same time somewhat to try and make my intention a little bit more clear.

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 19, 2004 8:55 PM
Reply to this message Reply
> Thank you for pointing that out Michael! The Artima.com
> Blog entry editor has problems with < >.
>
> I modified the code and article at the same time somewhat
> to try and make my intention a little bit more clear.


Great. I understand it now but... well, any programmer I work with would have to show me (in a profiler) that he's bottlenecking in event dispatch for this to feel like a win, and even then I suspect I'd look at inlining functions before reaching this far into the bag of tricks.

I hope you consider this a deep bag of tricks item. It looks like a very "around the block to get next door" way to get past the fact that normal compilation is single pass.. and we don't want to declare inline functions.. and "event dispatch is slower than latency because we're writing a disk controller in C++" sort of thing. Or am I missing something?

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 19, 2004 9:47 PM
Reply to this message Reply
> Great. I understand it now but... well, any programmer I
> work with would have to show me (in a profiler) that he's
> bottlenecking in event dispatch for this to feel like a
> win, and even then I suspect I'd look at inlining
> functions before reaching this far into the bag of tricks.

There is more to it than just a performance enhancement. A very big advantage is that that you don't have to register function pointers, as you would with other event handling mechanisms.

I have not done a good job of doing the technique justice. I have also realized that I can do the entire thing using template function specializations. This makes it a much more elegant technique than I first described.

I am currently preparing an example using the Windows Message handling API, so stay tuned for a much better and less phony example.

Matt Gerrans

Posts: 1153
Nickname: matt
Registered: Feb, 2002

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 20, 2004 10:44 AM
Reply to this message Reply
Maybe you could present this technique (or the one you mention that will supplant it) along with an implementation using the observer pattern and contrast the two. Then you could explain the specific advantages. From what I see here, I'd concur with Michael -- a solution that is less magical is also easier for the (code) reader to follow.



Posts: 5
Nickname: ephelon
Registered: Oct, 2004

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 20, 2004 11:23 AM
Reply to this message Reply
This might have some extremely limited application in DSP type code where response event response time is highly critical. I don't see that there is a practical application for this in GUI code.

The biggest difficulty is in the visibilty of the event handlers. The entire switch statement needs to be visible in the header. This switch has to be compiled after any handler templates has been specialized, and the switch statement itself won't scale very well.

The design does not lend itself to the loose coupling that GUI programming requires. The Qt library (a favorite of mine) uses virtual methods to handle events. Most events will likely cause a signal to be fired which (IIRC) involves several function calls and two string lookups. Compared to function pointers this is quite slow. Compared to the actual work done by most handlers, the overhead is almost nonexistant.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 20, 2004 6:40 PM
Reply to this message Reply
> I don't see that there is a practical
> application for this in GUI code.

I have just posted an article to http://www.codeproject.com/useritems/winevent.asp which shows an example program with Windows API code.

> The biggest difficulty is in the visibilty of the event
> handlers. The entire switch statement needs to be visible
> in the header. This switch has to be compiled after any
> handler templates has been specialized,

So what is the problem with that?

> and the switch
> statement itself won't scale very well.

Why not? (please reference the article if you can)

> The design does not lend itself to the loose coupling that
> GUI programming requires.

Again why not? I don't see how my technique is any more coupled than a dispatch mechanism based on virtual functions.

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 20, 2004 8:16 PM
Reply to this message Reply
So, what if we initialized the MsgHandler array with a default handler and removed the angle brackets in the following code:

template<>
LRESULT OnMsg<WM_CREATE>(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
strcpy(buf, "Press any key to start test");
return DefWindowProc(hWnd, WM_CREATE, wParam, lParam);
}

MsgHandlers[WM_CREATE] = &OnMsg<WM_CREATE>;

So that it looks like this:

LRESULT OnMsgWM_CREATE(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
strcpy(buf, "Press any key to start test");
return DefWindowProc(hWnd, WM_CREATE, wParam, lParam);
}


MsgHandlers[WM_CREATE] = &OnMsgWM_CREATE;

Seems a little cleaner with templates. It's a better example. On the other hand, it seems like you can have only one handler for each message. Not much chance to swap them out.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 20, 2004 8:44 PM
Reply to this message Reply
You added a line to my example, which is redundant:

> MsgHandlers[WM_CREATE] = &OnMsg<WM_CREATE>;

It is done in advance by the library itself. Whereas in the example you provide, the related line is neccessary:

> MsgHandlers[WM_CREATE] = &OnMsgWM_CREATE;

> Seems a little cleaner with templates. It's a better
> example. On the other hand, it seems like you can have
> only one handler for each message. Not much chance to
> swap them out.

That is true, there is no dynamic swapping of event handlers possible with the template example. That I perceive to be the tradeoff. It surprised me, but as others suspected, there is no noticeable performance gain in the win32 gui environment. I will be conducting more performance tests, in mock dispatch scenarios.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 20, 2004 10:39 PM
Reply to this message Reply
> along with an implementation using the observer pattern and contrast the two. Then you could explain the specific advantages.

I have completed a new post to my blog ( http://www.artima.com/weblogs/viewpost.jsp?thread=85301 ) which contrasts the template specialization technique with function pointers. I chose function pointers over the observer pattern because I am more familiar with it.

Matt Gerrans

Posts: 1153
Nickname: matt
Registered: Feb, 2002

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 21, 2004 5:31 AM
Reply to this message Reply
Hmm. This template trick seems like it might be useful for something, but I'm not convinced event dispatch is it, especially after the Windows examples (the articles on CP).

Comparing it to the function pointers technique is a bit of a strawman argument, since that wasn't a common way of dealing with the message cracking in the first place; both Microsoft and Borland used a combination of inheritance and message mapping macros. The macros were admittedly clunky.

One of the advantages of using inheritance is that you get nice IDE support. Another is that you don't have to remember the names of defined constants (that is, you work with names like OnResize() instead of WM_SIZE) and in some cases you can give them more meaningful names. Another is that the base class can split out the LPARAMs and WPARAMs into more meaningful parameter names (in some cases, either or both of these may contain several fields). In some other cases (eg. WM_POWERBROADCAST) a "main" message contains sub-messages; in this case it would be nicer if the libary normalized those sub-messages. That is, a base class might contain OnStandByRequested(), OnSuspend(), OnResume(), etc. instead of a single OnPowerBroadcast() that requires you to decode the WPARAM.

I think part of the problem with the template magic stuff is that it makes the code a little more opaque and less explicit. Clarity is probably more important than niftiness. It seems like this technique isn't much better than the big old switch statement, except for the coolness factor of using a template -- and I trust that big array of method references was automatically generated! ;) By the way, I suspect there is a more dynamic way to do it than having to define an array that way. What if the constant values were all over the map instead?

From a pragmatic standpoint, I still occasionally have a need to write a very simple app "in the raw," but such apps are usually very specific and simple, so they only handle a few messages. For these, it isn't necessary or desirable to use anything too tricky -- just a simple switch that passes work off to a few functions. For a more advanced app, the IDE is critical, so I would use Borland C++ Builder (and its virtual function + message mapping macros) or C#, which has events as first-class citizens.

By the way, I was a little mystified by the direct sending of the WM_TIMER messages via PostMessage(). Why didn't you instead create a real timer with SetTimer()?



Posts: 5
Nickname: ephelon
Registered: Oct, 2004

Re: Event Driven Programming using Template Specializations in C++ Posted: Dec 21, 2004 11:59 AM
Reply to this message Reply
> I have just posted an article to
> http://www.codeproject.com/useritems/winevent.asp which
> shows an example program with Windows API code.

It's very pretty. Looks clean, and probably works well for simple apps with few windows. You've thrown out the best part of using C++ for handling events, though! Messages are not being delivered directly to the object that is responsible for the window.

> > The biggest difficulty is in the visibilty of the event
> > handlers. The entire switch statement needs to be
> visible
> > in the header. This switch has to be compiled after
> any
> > handler templates has been specialized,
>
> So what is the problem with that?

My compile times are already long enough. If every translation unit has to compile several templates and deal with specialisation, things could get right out of hand. Add a new event; rebuild the whole enchilada.

> > and the switch
> > statement itself won't scale very well.
>
> Why not? (please reference the article if you can)

I didn't mean that the switch wouldn't scale performance-wise. It's not scalable maintenance-wise; the problem lies with adding more events. A lot of different events go off in a GUI app, and I often use custom events for communication between objects. Every one of these is going to require me to modify some switch statement.

In your example on CP, you have eliminated the switch statement, but you got the function pointers back.

> > The design does not lend itself to the loose coupling
> that
> > GUI programming requires.
>
> Again why not? I don't see how my technique is any more
> coupled than a dispatch mechanism based on virtual
> functions.

I have several lookup dialogs in my app. These use a custom table class, which is a subclass of a scroll view class, which is a subclass of a widget class. Many events are received by such a system, and processed by the part of the system that is responsible for that type of event. Many of these messages will result in a virtual function being called which might do something else in a base class.

I can't change the way I handle some given message with event handling based on template specialization for just one object. Message-map macros may be ugly and cumbersome, but it makes it a lot easier to see mouse events on a canvas widget. Perhaps this could be accomplised by adding another template parameter (the subclass receiving the event.) It still doesn't seem a net win for GUI programming.

I think the power that templates, and especially techniques like specialisation, is simply fantastic. I think that this technique has promise in specialised areas such as DSP or embedded programming. For GUI programming, I have seen nothing more flexible and intitive than signals and slots. (http://doc.trolltech.com/3.3/signalsandslots.html)

Flat View: This topic has 12 replies on 1 page
Topic: Yet Another Recursive Descent Parser for C++ Previous Topic   Next Topic Topic: Comp. Sci. 101: Definition of Type

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use