2017-06-19 4 views
0

私はC++を学び、私がよく知っている他の言語のさまざまなイディオムを実装しようとしています。C++のテンプレートイベント型を持つセントラルイベントディスパッチャ

最近、複数のオブジェクトがN個の引数を取るイベントを登録でき、EventDispatcher :: dispatch(event_name)を1回呼び出すと登録されたすべてのイベントコールバックが呼び出される中央のEventDispatcherを実装しようとしました。

まず、イベントのテンプレートクラスを作成しました。私は次の例のようなイベントを定義することができ、この構造により

template <typename ..._args> 
class Event 
{ 
public: 
    //Alias for template callback 
    using _callback = std::function<void(_args...)>; 

    //Ctor & Dtor 
    explicit Event(std::string const& name, _callback const& cb) : _name(name), _cbFunc(cb) {} 
    ~Event() {} 

    //Accessors 
    std::string const& getName() const { return this->_name; } 

    //Methods 
    void trigger(_args... a) { this->_cbFunc(a...); } 
private: 
    //Event identifier 
    std::string _name; 
    //Event callback - can't be changed inside event. 
    _callback const _cbFunc; 
}; 

void testCallback(int a) { 
    std::cout << a << std::endl; 
} 

Event<int> _event("TestEvent", std::bind(&testCallback, _1)); 
_event.trigger(20); //This will actually output 20 to the console. 

私は、隣にやりたいように定義されるべきEventDispatcherクラスがあります。

class EventDispatcher 
{ 
public: 
    EventDispatcher(); 
    ~EventDispatcher(); 

    void registerEvent(**Event of any type**); 
    void dispatchEvent(std::string const& eventName, ....args); 
private: 
    std::map<std::string, std::vector<**Event of any type**>> _eventList; 
}; 

私はこのように電話をかけることができます。

EventDispatcher _dispatcher; 
_dispatcher.registerEvent(_event); //event defined in the previous example. 

_dispatcher.dispatchEvent("TestEvent", 20); //this should call event's trigger() method with arguments passed to it actually. 

私は、中央ディスパッチャを実装し、それをテンプレート化イベントのインスタンスを登録し、その後は、dispatchEvent(...)に可変数の引数を渡すと、それはすべてのトリガー作ることができる持ってする方法を見つけ出すことができませんでしたがテンプレート化されたイベントのベクトルのイベント。

どうすればこのような機能を実現できますか?このようなシステムを実装するC++の方法から、私の思考のプロセスが正しいのか、それとも遠いのでしょうか?あらゆるヒントを歓迎します。

+0

間違ったタイプの引数を使用してイベントにディスパッチしようとするとどうなりますか? A.DLLの型に右値を取るイベントを登録し、B.DLLの型のインスタンスで呼び出すとどうなりますか? 2つのスコープのいずれかにB.DLLタイプからA.DLLタイプへの変換演算子がある場合はどうなりますか?これらは3つの異なる質問です。コンパイル時に型エラーが発生すると、型の不一致が発生したときに異なるシグネチャを扱う中央のディスパッチャが必要なのはなぜですか? – Yakk

+0

私はC++で初心者で、勉強したいと言ったような、すばらしい質問です。私がセントラルイベントディスパッチャを使用する理由は、それを使用できるようにすることです。別のクラスでは、例えばメインアプリケーションモジュールの「ディスパッチャ」に登録できます。他にどんな選択肢がありますか? – Alaminut

答えて

0

私は、イベントクラスの抽象基本クラス(IEvent)(Event) と可変長引数とdynamic_castとテンプレートディスパッチャ方法で溶液を示唆しています。

抽象メソッド getName

Apstrac基本クラス:

class IEvent 
{ 
public: 
    virtual const std::string & getName() const = 0; 
}; 

あなたのイベントクラスIEvent由来:

template <typename ..._args> 
class Event : public IEvent 
{ 
public: 
    //Alias for template callback 
    using _callback = std::function<void(_args...)>; 

    //Ctor & Dtor 
    //template< typename T_CB > 
    explicit Event(const std::string & name, const _callback & cb) : _name(name), _cbFunc(cb) {} 
    ~Event() {} 

    //Accessors 
    virtual const std::string & getName() const override { return this->_name; } 

    //Methods 
    void trigger(_args... a) { this->_cbFunc(a...); } 
private: 
    //Event identifier 
    std::string _name; 
    //Event callback - can't be changed inside event. 
    _callback const _cbFunc; 
}; 

テンプレートメソッドとディスパッチャ:

class EventDispatcher 
{ 
public: 
    EventDispatcher() {} 
    ~EventDispatcher() 
    { 
     for (auto el : _eventList) 
     { 
     for (auto e : el.second) 
      delete e; 
     } 
    } 

    void registerEvent(IEvent *event) 
    { 
     if (event) 
      _eventList[event->getName()].push_back(event); 
    } 

    template <typename ..._args> 
    void dispatchEvent(const std::string & eventName, _args...a) 
    { 
     auto it_eventList = _eventList.find(eventName); 
     if (it_eventList == _eventList.end()) 
     return; 
     for (auto ie : it_eventList->second) 
     { 
     if (Event<_args...> * event = dynamic_cast<Event<_args...>*>(ie)) 
      event->trigger(a...); 
     } 
    } 

private: 
    std::map<std::string, std::vector<IEvent*>> _eventList; 
}; 

そして最後にアプリケーション:

void testCallback(int a) { 
    std::cout << a << std::endl; 
} 

int main() 
{ 
    EventDispatcher eventDisp; 
    eventDisp.registerEvent(new Event<int>("TestEvent", &testCallback)); 

    eventDisp.dispatchEvent("TestEvent", 20); 

    return 0; 
} 
+0

誰かがdoubleを指定してディスパッチイベントを呼び出すと、intイベントは自動的に呼び出されません。一般に、コード内の異なる場所で推定された型の関数引数を使用して、正確な型の一致が壊れやすいので避けるべきです。 – Yakk

+0

この例では、次のエラーでコンパイルに失敗します。エラーLNK2019:未解決の外部シンボル "public:void __thiscall EventDispatcher :: dispatchEvent (class std :: basic_string 、class std :: allocator > const&、int) "(?? $ dispatchEvent @ H @ EventDispatcher @@ QAEXABV?$ basic_string @ DU?$ char_traits @D @ std @@ V?$ allocator @D @ 2 @ std @@ H @ Z)機能_main – Alaminut

+0

ああ、気にしないで、私の間違い..定義されたディスパッチャーは、別の.cppファイルです。 – Alaminut

関連する問題