さて、私が取り組んだプロジェクトは、C ++でイベントを処理するメカニズムをC#のイベントやEventHandlerに似たものにするための試みでした。そこで、オンラインでいくつかのテキストを調べて適切な方法を見つけた後、2つのテンプレートクラスを作成しました。1つはメンバーメソッドへのポインタを格納し、それを「EventHandler」と呼びます。もう1つは、イベントハンドラーのマップを格納し、必要に応じてそれらを呼び出すことです。これは「イベント」クラスです。次に、2つの「通常の」クラスを作成しました。1つはイベント発生クラスで、もう1つは応答クラスまたはリスニングクラスです。EventクラスとEventHandlerクラスの初期バージョンは次のとおりです。
#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
}
次に、新しいEventHandlerオブジェクトを作成するときに、明示的な'&lt;…..>'テンプレート構文を削除したかったのですが、これは明らかにC#の場合であるため、次のように単純に作成しました。
typedef EventHandler<MyClass> SomeEventFired_EventHandler;
そのため、このテンプレートの新しいオブジェクトを作成する必要がある場合は、次のように記述する必要があります。
MyClass* myObject = new MyClass();
MyEventFiringClass* firingObject = new MyEventFiringClass();
firingObject->OnFired += new SomeEventFired_EventHandler(myObject, &MyClass::SomeMethod);
もちろん、後で完全なコード例を使用すると、より明確になります。ここに私の質問がありました。MyClassの派生クラスのオブジェクトを渡すことができるようにしたかったのです。問題は、上記のEventHandlerのテンプレートがそのような派生オブジェクトを受け入れないことでした。これは、基本クラスのオブジェクトのみを予期しており、コンパイラが派生クラスから基本クラスに変換できないと文句を言ったためです。そして、ここにecatmurの貴重な助けがあります。彼は、EventHandlerクラスのコンストラクターをテンプレート化する適切な方法を教えてくれました。このように、MyClassを基本クラスとして使用して定義済みのSomeEventFired_EventHandlerと入力すると、オブジェクトがMyClassから派生したクラスからのものである限り、任意のオブジェクトとそのメソッドをコンストラクターに渡すことができるようになりました。これは、EventHandlerの多態的な機能を実現するための私の究極の目的でした。この機能が必要だったのは、C#でEventHandlerを確認すると、System :: EventHandlerがポリモーフィックであり、私が推測するように、本質的にクラスObjectから派生したクラスからさまざまなオブジェクトを受け入れることがわかるためです。ですから、これがecatmurソリューションに基づいて修正されたEventHandlerクラスを使用した完全な例であり、皆さんがレビューできるようになっています。最終的に、BaseEventHandlerクラスから派生して、派生したEventHandlerがさまざまな戻り型とさまざまな引数パラメーターを持つメソッドを格納できるようにすることができます。これは、ここに示されている基本的なメソッドがvoidを返し、voidを取得するメソッドを受け入れるためです( std ::function<>の宣言を変更する
std::function<int(int)>
、等々)。
イベントクラスは上記と同じです…</p>
#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
}
イベント発生クラスヘッダーファイル…</p>
#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
}
そのソースファイル、.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;
}
フォントクラスのイベントを処理する抽象基本クラス…</p>
#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
}
最初にIFontClient…ヘッダーファイルから派生したクラス
#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
}
そのソースファイル.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"));
}
}
アプリをテストするためのメインエントリ
#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;
}