2012-01-19 7 views
3

私が今作っているアプリケーションでは、Eventクラスで動作するEventDispatcherクラスがあります。ディスパッチャはテンプレート化されておらず、各イベントのランタイムタイプで動作します。これは、スクリプトが基本のEventクラスから継承して、独自のタイプのイベントを作成できるようにするためです。C++のカスタム実行時型システム/ライブラリ

このイベントディスパッチャーは、イベントの継承も処理したいと考えています。たとえば、FooBaseEventから継承するFooEventがあります。 FooEventが発生するたびに、FooBaseEventに関心のあるコールバックも通知されますが、それ以外の方法では通知されません。

これを簡単にするライブラリはありますか?継承チェックは、スクリプトで定義されたイベントにも拡張する必要があることに注意してください。

(。スクリプト言語はPythonのですが、それはあまり重要ではありません)


編集:EventDispatcherは、次のインターフェイス(パイソン)があります。私は

class EventDispatcher: 
    def subscribe(self, event_type, callback) -> EventDispatcher.Subscription 
    def post(self, event) 

    class Subscription: 
     def cancel(self) 
     def alive(self) -> bool 
+0

おそらく、ZeroC ICEで何かできますか?これはC++とPythonのバインディングを持っています。 –

答えて

0

をあなたの人生を楽にしてくれる図書館を知らず、それが存在しないことを証明していません。つまり、PythonとC++の型システムにはいくつかの重要な違いがあります。そのため、汎用型の情報システムを見つけるのは少し難しいかもしれません。

継承階層を追跡したいだけで、手動で型を登録する準備ができている場合は、比較的迅速に自分自身をロールすることができます。次の例では、type_registry_tの階層を記録し、次にdispatcher_tは階層を参照して、リスナーがイベントに関心があるかどうかを確認します。 NoteはいくつかのC++ 11機能を使用しています。

#include <iostream> 
#include <memory> 
#include <set> 
#include <string> 
#include <map> 
#include <vector> 

typedef std::string class_id_t; 

class type_registry_t { 
    std::multimap<class_id_t, class_id_t> parent_; 
public: 
    void register_type(class_id_t const& id, std::vector<class_id_t> const& parent) 
    { 
     for (size_t i = 0, sz = parent.size(); i < sz; ++i) 
      parent_.insert(std::make_pair(id, parent[i])); 
    } 

    template <class out_t> 
    out_t all_parents(class_id_t const& id, out_t out) const 
    { 

     for (auto r = parent_.equal_range(id); r.first != r.second; ++r.first) { 
      *out++ = r.first->second; 
      out = all_parents(r.first->second, out); 
     } 

     return out; 
    } 
}; 

class event_t { 
public: 
    virtual class_id_t id() const = 0; 
    virtual std::vector<class_id_t> parent() const = 0; 
}; 

inline void register_type(type_registry_t& r, event_t const& e) 
{ 
    r.register_type(e.id(), e.parent()); 
} 

class listener_t { 
    std::vector<class_id_t> listen_for_; 

protected: 
    listener_t(std::vector<class_id_t> const& listen_for) 
    : listen_for_ (listen_for) 
    { } 

public: 

    std::set<class_id_t> listen_for(type_registry_t const& reg) const 
    { 
     std::set<class_id_t> s; 
     for (size_t i = 0, sz = listen_for_.size(); i < sz; ++i) { 
      s.insert(listen_for_[i]); 
      reg.all_parents(listen_for_[i], std::inserter(s, s.end())); 
     } 
     return s; 
    } 

    virtual void notify(event_t const&) = 0; 
}; 

class dispatcher_t { 
    type_registry_t const* reg_; 
    std::vector<std::shared_ptr<listener_t>> listener_; 
public: 
    dispatcher_t(type_registry_t const& reg) 
    : reg_ (&reg) 
    { } 

    void connect(std::shared_ptr<listener_t> const listener) 
    { 
     listener_.push_back(listener); 
    } 

    void signal(event_t& event) 
    { 
     class_id_t const id = event.id(); 
     for (size_t i = 0, sz = listener_.size(); i < sz; ++i) { 
      std::set<class_id_t> const s = listener_[i]->listen_for(*reg_); 

      if (s.find(id) != s.end()) 
       listener_[i]->notify(event); 
     } 
    } 
}; 

これにより、階層内の位置に基づいてイベントの選択を実行できます。次のように(これはあなたがあなたの例で説明していると思います)。

struct foo_base_event_t : event_t { 
    class_id_t id() const { return "foo_base_event_t"; } 
    std::vector<class_id_t> parent() const 
    { 
     std::vector<class_id_t> r; 
     r.push_back("event_t"); 
     return r; 
    } 
}; 

struct foo_event_t : foo_base_event_t { 
    class_id_t id() const { return "foo_event_t"; } 
    std::vector<class_id_t> parent() const 
    { 
     std::vector<class_id_t> r; 
     r.push_back("foo_base_event_t"); 
     return r; 
    } 
}; 

struct foo_event_listener_t : listener_t { 
    static std::vector<class_id_t> relevant_ids() 
    { 
     std::vector<class_id_t> r; 
     r.push_back("foo_event_t"); 
     return r; 
    } 

    foo_event_listener_t() 
    : listener_t (relevant_ids()) 
    { } 

    void notify(event_t const& e) 
    { 
     std::cout << "foo_event_listener_t::notify() with " << typeid(e).name() << " " << (void*)&e << "\n"; 
    } 
}; 

struct foo_base_event_listener_t : listener_t { 
    static std::vector<class_id_t> relevant_ids() 
    { 
     std::vector<class_id_t> r; 
     r.push_back("foo_base_event_t"); 
     return r; 
    } 

    foo_base_event_listener_t() 
    : listener_t (relevant_ids()) 
    { } 

    void notify(event_t const& e) 
    { 
     std::cout << "foo_base_event_listener_t::notify()" << typeid(e).name() << " " << (void*)&e << "\n"; 
    } 
}; 

int main() 
{ 
    type_registry_t reg; 

    reg.register_type("event_t", std::vector<class_id_t>()); 
    reg.register_type("foo_base_event_t", std::vector<class_id_t>(1, "event_t")); 
    reg.register_type("foo_event_t", std::vector<class_id_t>(1, "foo_base_event_t")); 

    dispatcher_t dispatcher (reg); 
    dispatcher.connect(std::shared_ptr<listener_t>(new foo_event_listener_t())); 
    dispatcher.connect(std::shared_ptr<listener_t>(new foo_base_event_listener_t())); 

    foo_base_event_t foo_base_event; 
    dispatcher.signal(foo_base_event); 

    foo_event_t foo_event; 
    dispatcher.signal(foo_event); 


    return 0; 
} 

イベントタイプの登録を許可するには、好みの方法でPythonにこの一部を公開する必要があります。私はエラーチェックを含まず、class_idの各組をlisten_for()への呼び出しを構築するのはおそらく遅いでしょう。

+0

ありがとうございました。私はすでにこれに対する解決策を実装しましたが、質問を更新しませんでした。私は自分自身の解決策を記述した回答を投稿しました。 –

+0

ようこそ。あなたのコードを投稿するための歓声。類似点と相違点を検討することは興味深い。あなたのことを見ると、私はマクロを使って提案するのを忘れていました。このような状況では、多くの繰り返しコードを短縮することができます。私は最近、可変的なマクロを認識しました。コンパイラがそれらをサポートしている場合は、親リストを定義すると便利です。 http://stackoverflow.com/questions/679979/how-to-make-a-variadic-macro-variable-number-of-arguments –

+0

はい、それは「父親」がすることです。 :)(あなたがしたい場合は、バリデーションのマクロパラメータに名前を付けることができます。) –

関連する問題