2012-01-19 2 views
3

현재 응용 프로그램에서 Event 클래스로 작동하는 EventDispatcher 클래스가 있습니다. 발송자는 템플릿 처리되지 않으며 각 이벤트의 런타임 유형과 함께 작동합니다. 이것은 스크립트가 기본 Event 클래스로부터 상속하고 그들 자신의 타입의 이벤트를 만드는 것을 허용하기위한 것입니다.C++ 용 사용자 정의 런타임 유형 시스템/라이브러리

이 이벤트 디스패처는 이벤트 상속을 처리하기를 원합니다. 예를 들어, 나는 을 상속받은 FooEvent입니다. FooEvent이 발생할 때마다 FooBaseEvent에 관심이있는 콜백이 통보되지만 그 반대의 경우는 아닙니다.

더 쉽게 만들 수있는 라이브러리가 있습니까? 상속 검사는 스크립트에 정의 된 이벤트로 확장되어야한다는 것을 기억하십시오.

(스크립팅 언어는 파이썬하지만, 그 문제가 너무 많이는 안된다.)


편집 :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

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

예, 그것이 '부모님 ...'입니다. :) (원하는 경우 가변적 인 매크로 매개 변수의 이름을 지정할 수 있습니다.) –