2012-01-19 40 views
3

在我目前正在製作的應用程序中,我有一個EventDispatcher類,它與基類Event類一起使用。調度程序不是模板化的,它適用於每個事件的運行時類型;這是爲了允許腳本從基類Event繼承並創建它們自己的類型的事件。用於C++的自定義運行時類型系統/庫

它希望事件調度程序也處理事件的繼承。例如,我有FooEvent,它繼承自FooBaseEvent;每當發生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查看層次結構以查看偵聽器是否對事件感興趣。注意使用一些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

是的,這就是'PARENTS ...'所做的。 :)(你可以命名可變宏參數,如果你想。) –