2012-01-19 2 views
3

В приложении, которое я делаю на данный момент, у меня есть класс EventDispatcher, который работает с базой Event. Диспетчер не является шаблоном, он работает с типами времени выполнения каждого события; это позволяет сценариям наследовать от базового класса Event и создавать собственные типы событий.Пользовательская система/библиотека типа времени выполнения для C++

Он хотел бы, чтобы этот диспетчер событий обрабатывал наследование событий. Например, у меня есть FooEvent, который наследуется от FooBaseEvent; всякий раз, когда происходит FooEvent, обратные вызовы, заинтересованные в FooBaseEvent, также уведомляются, но не наоборот.

Есть ли какая-нибудь библиотека, которая упростит это? Помните, что проверка наследования должна быть расширена и для событий, определенных в сценариях.

(язык сценариев Python, но это вопрос не должен иметь так много.)


Edit: My EventDispatcher имеет следующий интерфейс (Python):

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

I «Я не знаю ни одной библиотеки, которая облегчит вашу жизнь, но это не доказывает, что ее не существует. Тем не менее, система типов 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

Да, это то, что делает «РОДИТЕЛИ ...». :) (Вы можете назвать переменные параметры макроса, если хотите.) –

Смежные вопросы