Click here to Skip to main content
Click here to Skip to main content

A native C++ implementation of the .NET delegate pattern

, 4 Apr 2005
Rate this:
Please Sign up or sign in to vote.
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:

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_:

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:

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:

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:

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:

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:

(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:

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

Share

About the Author

No Biography provided

Comments and Discussions

 
Generalother solutions PinmemberGoran Mitrovic4-Apr-05 22:34 
General.. and actually delegates come from Delphi, not J++ PinmemberDon Clugston4-Apr-05 15:44 
GeneralRe: .. and actually delegates come from Delphi, not J++ PinmemberJim Crafton5-Apr-05 19:44 
GeneralRe: .. and actually delegates come from Delphi, not J++ PinmemberJörgen Sigvardsson20-Feb-06 10:51 
GeneralAnother implementation + some comments PinmemberDon Clugston4-Apr-05 15:36 
GeneralRe: Another implementation + some comments PinmemberTutu4-Apr-05 18:21 
GeneralRe: Another implementation + some comments PinmemberChristos Malliopoulos5-Apr-05 1:14 
GeneralRe: Another implementation + some comments PinmemberDon Clugston5-Apr-05 14:59 
GeneralRe: Another implementation + some comments PinmemberJim Crafton5-Apr-05 19:47 
GeneralRe: Another implementation + some comments PinmemberDon Clugston5-Apr-05 20:22 
GeneralRe: Another implementation + some comments PinmemberP.GopalaKrishna22-Jun-05 17:28 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411022.1 | Last Updated 4 Apr 2005
Article Copyright 2005 by Christos Malliopoulos
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid