Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#

A Native C++ Implementation of the .NET Delegate Pattern

Rate me:
Please Sign up or sign in to vote.
4.30/5 (12 votes)
4 Apr 20053 min read 69.5K   456   32   11
An implementation of the .NET delegate pattern using C++ templates

Introduction

Back in 1998 - or even earlier - Microsoft introduced in its Visual J++ language the delegate as a flexible alternative to Sun's Java subscriber pattern for handling events generated by visual elements like buttons and text boxes. In fact, the delegate has been a matter of controversy between Microsoft and Sun as the latter never accepted it to be part of the Java Standard. For a verbose yet informative description of delegates and Java subscriber pattern, please refer to Chris Sells' article .NET Delegates: A C# Bedtime Story.

In this article, we present an implementation of the delegate pattern using C++ templates. In the accompanying demonstration project, we provide a simplistic emulation of an event source object - that we call Button - firing a Click event, together with an object we call ButtonContainer that defines a click event handler and registers it with the Button event source.

The Delegate Pattern

The .NET delegate pattern is comprised of three things:

  • An event handling function which can be an instance or static class method.
  • An event-handler wrapper, designated in C# with the delegate keyword.
  • The event, this is a collection of delegate objects with a += operator overload plus certain access restrictions so that a client cannot fire the contained events explicitly.

In a typical operating scenario, a class EH wishing to handle the event X generated by class ES, defines a handler with a signature appropriate for the event it wishes to handle. In Windows-Forms C# code, this is declared as follows:

C#
class EH {
    ES es_;
    ...
    void EventX_Handler(Object sender, EventArgs e) { ... }
    ...
}

Then the code registers a delegate object of type EventHandler, with the X event of EH.es_:

C#
class EH {
    ES es_;
    ...
    void EventX_Handler(Object sender, EventArgs e) { ... }
    ...
    
    public EH() {
        ...
        es_.eventX += new EventHandler(EventX_Handler);
        ...
    }
}

The C++ Template Implementation and Demonstration Project

First, we create a base template class for delegates with a two-argument signature: the sender object and some custom type that designates the event arguments:

C++
template<typename _Arg1, typename _Arg2> struct ClosureBase {
    virtual void operator ()(_Arg1 &arg1, _Arg2 &arg2) = 0;
};

In this, and in the following code, we prefer the word closure instead of delegate because the latter is reserved in C++.NET 2005. As you will see, it is an abstract base class for all delegates with two arguments.

The Button event source class can then be completely defined using this template only:

C++
class Button {
public:
struct ClickEventArguments {
    int xCoord_, yCoord_;
    ClickEventArguments(int xCoord = 0, int yCoord = 0) : 
                  xCoord_(xCoord), yCoord_(yCoord){ }
};

// 'typedef' to make your life easier
typedef vector<ClosureBase<Button, ClickEventArguments> *> ClickEvents;
// The delegates list. Define one per event
ClickEvents clickEvents_;

void FireClickEvents(int xc, int yc) {
    ClickEventArguments args(xc, yc);
    for(ClickEvents::iterator it = clickEvents_.begin(); 
                            it != clickEvents_.end(); it++) {
        (*(*it))(*this, args);
    }
}        
};

Only code relevant to the current discussion is presented here. As you can see, the Click event of Button class is a vector of ClosureBase pointers. The FireClickEvents method iterates through the event elements calling their () operator.

The ButtonContainer class contains a Button object and registers a Click event-handler:

C++
class ButtonContainer {
Button button_;

void ButtonClickHandler(Button &sender, Button::ClickEventArguments &arguments)
{
    cout << 
        "Delegate invoked with sender " << 
        typeid(sender).name() << ", and eventArgs (" << 
        arguments.xCoord_ << ", " << arguments.yCoord_ << ")" << endl;
}

public:
ButtonContainer()
{
    button_.clickEvents_.push_back(CreateClosure(this, 
             &ButtonContainer::ButtonClickHandler));
}                
};

The only "weird" thing is the CreateClosure in the ButtonContainer constructor. As one can imagine, CreateClosure is a generator for a ClosureBase derived template that is parameterized on the event arguments and the event-handling object:

C++
template<class _Ty, typename _Arg1, typename _Arg2> 
   class Closure : public ClosureBase<_Arg1, _Arg2> {
       void (_Ty::* method_)(_Arg1 &, _Arg2 &);
       _Ty *objectPtr_;

public:
    Closure(_Ty *objectPtr, 
       void (_Ty::* method)(_Arg1 &, _Arg2 &)) : objectPtr_(objectPtr) 
                   { method_ = method; }
    virtual ~Closure() { }
    virtual void operator ()(_Arg1 &arg1, _Arg2 &arg2) 
       { return (objectPtr_->*method_)(arg1, arg2); }
};

Closure template associates an object pointer with the event-handling method signature through the attributes method_ and objectPtr_. The first is a pointer to a class method and the second an object pointer. Then the () operator calls the ->*method_ of objectPtr_ object pointer:

C++
(objectPtr_->*method_)(arg1, arg2)

For related implementations, please see the code in the functional STL header.

Last, the creation of Closure objects is done through CreateClosure generator function, to make the code more readable at client site:

C++
template<class _Ty, typename _Arg1, typename _Arg2> 
    inline Closure<_Ty, _Arg1, _Arg2> 
       *CreateClosure(_Ty *object, void(_Ty::*method)(_Arg1 &, _Arg2 &)){
    return new Closure<_Ty, _Arg1, _Arg2>(object, method);
}

In the demo project, we have also included a WindowManager class that is supposed to emulate the operating system.

In a more complete demonstration, WindowManager should run in a background thread. The WindowManager is of course not relevant to our subject but it implements a very nice singleton (known as Meyers singleton).

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Greece Greece
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Generalother solutions Pin
Goran Mitrovic4-Apr-05 21:34
Goran Mitrovic4-Apr-05 21:34 
General.. and actually delegates come from Delphi, not J++ Pin
Don Clugston4-Apr-05 14:44
Don Clugston4-Apr-05 14:44 
GeneralRe: .. and actually delegates come from Delphi, not J++ Pin
Jim Crafton5-Apr-05 18:44
Jim Crafton5-Apr-05 18:44 
GeneralRe: .. and actually delegates come from Delphi, not J++ Pin
Jörgen Sigvardsson20-Feb-06 9:51
Jörgen Sigvardsson20-Feb-06 9:51 
GeneralAnother implementation + some comments Pin
Don Clugston4-Apr-05 14:36
Don Clugston4-Apr-05 14:36 
GeneralRe: Another implementation + some comments Pin
Corneliu Tusnea4-Apr-05 17:21
Corneliu Tusnea4-Apr-05 17:21 
GeneralRe: Another implementation + some comments Pin
Christos Malliopoulos5-Apr-05 0:14
Christos Malliopoulos5-Apr-05 0:14 
GeneralRe: Another implementation + some comments Pin
Don Clugston5-Apr-05 13:59
Don Clugston5-Apr-05 13:59 
GeneralRe: Another implementation + some comments Pin
Jim Crafton5-Apr-05 18:47
Jim Crafton5-Apr-05 18:47 
GeneralRe: Another implementation + some comments Pin
Don Clugston5-Apr-05 19:22
Don Clugston5-Apr-05 19:22 
GeneralRe: Another implementation + some comments Pin
Gopalakrishna Palem22-Jun-05 16:28
Gopalakrishna Palem22-Jun-05 16:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.