Well, the project that I worked upon was a trial to make a mechanism to handle events in C++ similar – more or less to C# way of Events and EventHandlers.
So, after research online and inside some texts and figuring out the suitable ways, I wrote two template classes:
One is to store a pointer for the member method and called it ‘EventHandler’.
The other one is to store a map of event handlers and to invoke them upon need; this is the ‘Event’ class.
Then I wrote two ‘normal’ classes: one to be the event firing class and the other to be the responding or listening class.
The initial versions of the Event and EventHandler classes were as follows:
#include <functional>
namespace eventhandling
{
#ifndef __BASIC_EVENT_HANDLER__
#define __BASIC_EVENT_HANDLER__
////////////////////////////////////////////////////////////////////////////
// the root event handler class
////////////////////////////////////////////////////////////////////////////
template <typename empty = int, empty = 0>
class BaseEventHandler
{
public:
virtual void Excute() = 0;
};
///////////////////////////////////////////////////////////////////////////
// the basic derived event handler class; the class which will wrap the
// methods of other classes which want to respond to specific event(s)..
///////////////////////////////////////////////////////////////////////////
template <typename ResponderType>
class EventHandler : public BaseEventHandler < >
{
typedef void (ResponderType::*MethodPointer());
ResponderType* responder;
MethodPointer methodPointer;
public:
EventHandler(ResponderType* resp, MethodPointer func) : responder(resp), methodPointer(func)
{}
void Excute()
{
return methodPointer();
}
};
#endif
}
#include "BasicEventHandler.h"
#include <map>
namespace eventhandling
{
#ifndef __BASIC_EVENT__
#define __BASIC_EVENT__
////////////////////////////////////////////////////////////////////////////////////////////////
// the event class which will receive these event handlers, stores them in a map object,
// and call them squentially when invoked from within the event firing method...
////////////////////////////////////////////////////////////////////////////////////////////////
// the template takes no parameters, so I added an empty parameter, just because
//it cannot ignore the parameter list, otherwise it will be considered template specialization
template <typename empty = int, empty = 0>
class BasicEvent
{
//store the eventhandlers in a map so that I can track them from outside the class by id
typedef std::map<int, BaseEventHandler<empty>* > Responders;
Responders responders;
int respondersCount;
public:
BasicEvent() : respondersCount(0)
{}
// classical add method templatized so that it accepts any object
template <typename Responder>
int Add(Responder* sender, void (Responder::*memberFunc)())
{
responders[respondersCount] = (new EventHandler<Responder>(sender, memberFunc));
respondersCount++;
return respondersCount - 1;
}
// simple method to clean up the map memory after done with the eventhandlers
void Remove(int responderID)
{
Responders::iterator it = responders.find(responderID);
if (it == responders.end())
return;
delete it->second;
responders.erase(it);
}
// method which invokes all the eventhandlers alltogether without control from the outside
void Invoke()
{
Responders::iterator it = responders.begin();
for (; it != responders.end(); ++it)
{
it->second->Excute();
}
}
// method which invokes only the eventhandler whose id has been passed to it
void Invoke(int id)
{
Responders::iterator it = responders.find(id);
if (it != responders.end())
it->second->Excute();
}
// overloaded operator+= to replace the functionality of method Add()
template <typename Responder>
void operator+=(EventHandler<Responder>* eventhandler)
{
responders[respondersCount] = eventhandler;
respondersCount++;
}
// overloaded operator -= to replace the functionality of method Remove()
void operator-=(int id)
{
Responders::iterator it = responders.find(id);
if (it == responders.end())
return;
delete it->second;
responders.erase(it);
}
//simple method which gives the size of the map object
int Size()
{
return respondersCount;
}
};
#endif
}
Then, I wanted to get rid of the explicit ‘<…..>’ template syntax upon creating a new EventHandler object, which is obviously what is the case in C#, so, I made it simply like the following:
typedef EventHandler<MyClass> SomeEventFired_EventHandler;
so that when I need to create a new object of this template, I just need to write:
MyClass* myObject = new MyClass();
MyEventFiringClass* firingObject = new MyEventFiringClass();
firingObject->OnFired += new SomeEventFired_EventHandler(myObject, &MyClass::SomeMethod);
Of course, the complete code example later will make it more clear!
Here came my question, where I wanted to be able to pass objects of derived classes of MyClass. The problem was that the template of EventHandler as shown above did not accept such derived objects, because it was expecting objects of the base class exclusively and the compiler complained that it cannot convert from the derived class to the base class.
And here comes the invaluable help of ecatmur, when he showed me the proper way of making the constructor of the EventHandler class templated.
That way, when I type defined SomeEventFired_EventHandler using MyClass as a base class, I became able to pass any object – and its method – to its constructor, as long as the object is from a class derived from MyClass. This was my ultimate aim in achieving the polymorphic feature of the EventHandler.
I wanted this feature, because if you check the EventHandlers in C#, you can see that System::EventHandler is polymorphic, that it accepts different objects from classes which are essentially derived from class Object as I guess.
So, here is the full example, with the rectified EventHandler class based on ecatmur solution, for you guys to review, and I hope you will find it of some benefit.
Eventually, you can derive from the BaseEventHandler class so that derived EventHandlers can store methods with different return types and different argument parameters, because the basic one that is shown here accepts methods which return void and take void (I believe you can do so by just changing the declaration of std::function<> to make it accept other types of methods, e.g.,
std::function<int(int)>
,and so on).
The event class is the same as above…
#include <functional>
namespace eventhandling
{
#ifndef __BASIC_EVENT_HANDLER__
#define __BASIC_EVENT_HANDLER__
////////////////////////////////////////////////////////////////////////////
// the root event handler class
////////////////////////////////////////////////////////////////////////////
template <typename empty = int, empty = 0>
class BaseEventHandler
{
public:
virtual void Excute() = 0;
};
///////////////////////////////////////////////////////////////////////////
// the basic derived event handler class; the class which will wrap the
// methods of other classes which want to respond to specific event(s)..
///////////////////////////////////////////////////////////////////////////
template <typename ResponderType>
class EventHandler : public BaseEventHandler < >
{
std::function<void ()> type_erased_method;
ResponderType* responder;
public:
template<typename T>
EventHandler(T* resp, void (T::*MethodPointer)()) : responder(resp), type_erased_method(std::bind(MethodPointer, resp))
{}
void Excute()
{
return type_erased_method();
}
};
#endif
}
The event firing class header file…
#include <iostream>
#include <string>
#include "BasicEvent.h"
namespace eventhandling
{
#ifndef __FONT_SIMULATOR__
#define __FONT_SIMULATOR__
typedef BasicEvent<> FontEvent;
typedef std::string s;
class FontSimulator
{
private:
s fontName;
s fontSize;
s fontStyle;
public:
FontSimulator();
FontSimulator(s name, s size, s style);
~FontSimulator();
FontEvent OnDraw;
void DrawText();
// the setting methods
void SetFontName(s n) {fontName = n;}
void SetFontSize(s si) {fontSize = si;}
void SetFontStyle(s st) {fontStyle = st;}
// the getting methods
s GetFontName() {return fontName;}
s GetFontSize() {return fontSize;}
s GetFontStyle() {return fontStyle;}
};
#endif
}
its source file, .cpp
#include "FontSimulator.h"
using namespace eventhandling;
FontSimulator::FontSimulator() : fontName("Default Name"), fontSize ("Default Size"), fontStyle("Default Style")
{
}
FontSimulator::FontSimulator(s fName, s fSize, s fStyle) : fontName(fName), fontSize(fSize), fontStyle(fStyle)
{
}
FontSimulator::~FontSimulator()
{
delete this;
}
void FontSimulator::DrawText()
{
std::cout << "Initialization of font done!" << std::endl << std::endl;
std::cout << fontName << std::endl;
std::cout << fontSize << std::endl;
std::cout << fontStyle << std::endl << std::endl;
for (int i = 0; i < OnDraw.Size(); ++i)
{
OnDraw.Invoke(i);
std::cout << "the #" << i + 1 << " responder method called!" << std::endl << std::endl;
std::cout << fontName << std::endl;
std::cout << fontSize << std::endl;
std::cout << fontStyle << std::endl << std::endl;
}
for (int j = 0; j < OnDraw.Size(); j++)
{
//OnDraw.Remove(j);
OnDraw -= j;
}
std::cout << "The finishing font work after all the event handler are called!" << std::endl <<std::endl;
}
The abstract base class which handles the event of the font class…
#include "BasicEventHandler.h"
namespace eventhandling
{
#ifndef __IFONT_CLIENT__
#define __IFONT_CLIENT__
class IFontClient
{
public:
IFontClient(){};
~IFontClient(){delete this;}
virtual void SetupFont() = 0;
};
typedef EventHandler<IFontClient> FontEventHandler;
#endif
}
The derived class from IFontClient… header file first
#include "BasicEventHandler.h"
#include "BasicEvent.h"
#include "FontSimulator.h"
#include "IFontClient.h"
namespace eventhandling
{
#ifndef __CONTROL_SIMULATOR__
#define __CONTROL_SIMULATOR__
class ControlSimulator : public IFontClient
{
protected:
std::string caption;
FontSimulator* font;
public:
ControlSimulator();
ControlSimulator(std::string theCaption, FontSimulator* theFont);
~ControlSimulator();
virtual void Draw();
virtual void SetupFont();
void SetCaption(std::string c) {caption = c;}
std::string GetCaption() {return caption;}
};
#endif
}
its source file .cpp
#include "ControlSimulator.h"
namespace eventhandling
{
ControlSimulator::ControlSimulator() : caption("Default Caption"), font(new FontSimulator())
{
}
ControlSimulator::ControlSimulator(std::string c, FontSimulator* f) : caption(c), font(f)
{
}
ControlSimulator::~ControlSimulator()
{
delete this;
}
void ControlSimulator::Draw()
{
std::cout << "Drawing " << caption << " is done!" << std::endl << std::endl;
}
void ControlSimulator::SetupFont()
{
std::string costumProperty = caption;
font->SetFontName(costumProperty.append(", Costumized Font Name"));
costumProperty = caption;
font->SetFontSize(costumProperty.append(", Costumized Font Size"));
costumProperty = caption;
font->SetFontStyle(costumProperty.append(", Costumized Font Style"));
}
}
The main entry to test the app
#include "ControlSimulator.h"
using namespace eventhandling;
int main(int argc, char** argv)
{
char c;
FontSimulator* font = new FontSimulator();
ControlSimulator* control1 = new ControlSimulator("Control one", font);
ControlSimulator* control2 = new ControlSimulator("Control two", font);
control1->Draw();
control2->Draw();
font->OnDraw += new FontEventHandler(control1, &ControlSimulator::SetupFont);
font->OnDraw += new FontEventHandler(control2, &ControlSimulator::SetupFont);
font->DrawText();
std::cout << "Enter any character to exit!" << std::endl;
std::cin >> c;
return 0;
}
DerivedClassthatDoSomethingis not virtual ?TemplateClassis whatstd::functionwas made for. I highly recommend to use it...