Как рассылать события в событии l oop подписчикам? - PullRequest
0 голосов
/ 16 июня 2020

Я пытаюсь достичь асинхронного события l oop, которое может хранить события в очереди и отправлять их позже разным подписчикам. Все события являются производными от одной базы, также все подписчики являются производными от своего собственного базового класса. События должны содержать разные типы ценностей. Подписчики должны направить их экземплярам других классов. Я могу использовать только библиотеку std.

У меня проблемы с отправкой событий разных типов в соответствующие методы подкласса подписчика. Если я сохраняю Event Base в очереди, я теряю информацию о типе производных событий и не могу вызывать правильные методы подписчика. Я также не хочу использовать If then else или switch в типе Info.

Я пробовал использовать шаблоны и создать для каждого события собственный метод отправки в классе подписчика или std::function, чтобы обернуть вызов метода в лямбда-функции.

Мой подход кажется крайне сложным и медленным. Я не могу перенаправить события в классы, которыми управляет подписчик. Я всегда получаю сообщение об ошибке при отсутствии соответствующего вызова функции. Я понимаю, почему, но не знаю, как решить мою проблему.

Использование:

observer obs;
EventLoop<observer,Event> el;

auto f = [&obs] (Event& e) {
    obs.dispatch((Up&)e);
};
el.subscribe<Up>(f);

auto g = [&obs] (belfsm::Event& e) {
    obs.dispatch((Down&)e);
};

el.subscribe<Down>(g);

Up a;
Down b;
el.sendEvent(a);
el.sendEvent(b);
el.dispatchF();

Что я пробовал до сих пор:

#include <map>
#include <vector>
#include <queue>
#include <typeindex>
#include <functional>
#include <algorithm>
#include <memory>

template<typename T_Subscribe, typename T_Event>
class EventLoop {
public:      
    typedef std::function<void(T_Event&)> eventHandler;

    EventLoop(){}
    virtual ~EventLoop(){}

    void dispatch2subscriber(){
        while(!eventQueue.empty()){
            std::type_index eventType = eventQueue.front().first;

            auto it_s = eventSubscriber.find(eventType);
            if( it_s != eventSubscriber.end() ){
                std::vector<T_Subscribe*> v = eventSubscriber[eventType];

                for(auto const& subscriber: v) {
                    subscriber->dispatch(eventQueue.front().second);
                }
        }

        eventQueue.pop();
    }

    void dispatchFunction(){
        while(!eventQueue.empty()){
            std::type_index eventType = eventQueue.front().first;

            auto it_s = eventSubscriberF.find(eventType);
            if( it_s != eventSubscriberF.end() ){
                std::vector<std::function<void(T_Event&)>> v = eventSubscriberF[eventType];

                for(auto const& subscriber: v) {
                    subscriber(eventQueue.front().second);
                }
            }

            eventQueue.pop();
        }//while
    }

    template<typename U>
    bool subscribe(T_Subscribe& subscriber){
        std::type_index eventType = getEventType<U>();
        eventSubscriber[eventType].push_back(&subscriber);
        return true;
    }



    template<typename U>
    bool subscribe(eventHandler subscriber){
        std::type_index eventType = getEventType<U>();

        eventSubscriberF[eventType].push_back(subscriber);
        return true;
}
private:
    //Event Queue
    std::queue<std::pair<std::type_index,T_Event&>std::deque<std::pair<std::type_index,T_Event&>>> eventQueue;
    //Subscriber map pointer to instance
    std::map<std::type_index,std::vector<T_Subscribe*>> eventSubscriber;
    //Subscriber map to member function
    std::map<std::type_index,std::vector< eventHandler > > eventSubscriberF;
}

Подписчики:

//Events
struct Event {};
struct Up : Event { int i };
struct Down : Event { int i };

//Class which uses events.
class another_class
{
   void react(Event &) {};
}

class yet_another_class : public another_class 
{
    void react(Event &) {};
    void react(Up &) {};
    void react(Down &) {};
}

//Manages other classes which uses events
class BaseSubscribery
{
public:
    std::unique_ptr<another_class> another_class_ptr;
}

class Subscriber: public BaseSubscriber
{
public:
    observer();
    template<typename E>
    void dispatch(E& event);

};

template<typename E>
void observer::dispatch(E& event) {
    *another_class_ptr->react(event);*
}

1 Ответ

0 голосов
/ 17 июня 2020

Этот код показывает, как обрабатывать объект, унаследованный от общего родителя, и dynamic_cast - от производного.

Есть один базовый интерфейс: event Есть три типа событий: event_one, event_two и event_no. Их топология следующая:

event_two -> event_one -> event

Итак, event_two наследуется от event_one, которое наследуется от event. И есть event_no («нет», потому что он не будет обработан), который наследуется напрямую от event.

event_no -> event

Мы создаем обработчик событий и передаем все производные к нему.

Ключевая часть - if (dynamic_cast<event_one *>(ievent)) в handler_one::handle_event, если нет преобразования времени выполнения в производный класс, это вернет нулевой указатель, поэтому вы знаете, что не можете справиться с ним . В противном случае он вернет нужный производный класс. У вас должна быть цепочка производных классов, наиболее производные в первую очередь, для правильной обработки всех случаев.

Я создал два обработчика, один частичный (handler_one), который обрабатывает события двух типов: event_one и event_two (event_two отсутствует в if, но, поскольку это event_one, он будет обработан и вызвана правильная функция; если вы не определяете функцию guid, в производном, то в event_one будет называться); и второй (handler_two), который обрабатывает их все.

Что касается очередей, вы можете хранить event указателей и забыть о производных классах.

EDIT:

Теперь управляется шаблонами. Нет if или switch. Вы можете добавить другие типы событий, специализирующиеся на шаблоне элемента.

Вывод программы:

manuel@desktop:~/projects$ g++ -Wall main.cc -o main -std=c++11 && ./main 
Handler 1
Event guid: abcd (id=1)
Event guid: abcd with some custom stuff (id=2)
Event no guid: ugh! this is unhandled (id=-1)
End

Программа:

//
#include <typeindex>
#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <functional>

class event
{
public:
    event () : _id (0) {}
    virtual ~event () {}
    virtual int id () { return _id; }
protected:
    int _id;
};

class event_one : public event
{
public:
    event_one () : _guid ("abcd") { _id = 1; }
    ~event_one () {}
    virtual std::string guid () { return _guid; }
protected:
    std::string _guid;
};

class event_two : public event_one
{
public:
    event_two () { _id = 2; }
    ~event_two () {}
    virtual std::string guid () { return _guid + " with some custom stuff"; }
protected:
};

class event_no : public event
{
public:
    event_no () : _guid ("ugh!") { _id = -1; }
    ~event_no () {}
    virtual std::string guid_no () { return _guid + " this is unhandled"; }
protected:
    std::string _guid;
};

class handler_one
{
public:
    template<typename Event>
    int handle_event (Event * ievent);
};

template<typename Event>
int handler_one::handle_event (Event * ievent) {
    std::cout << "Event guid: " << ievent->guid()
              << " (id=" << ievent->id() << ")" << std::endl;
    return 0;
}

template<>
int handler_one::handle_event<event_no> (event_no * ievent) {
    std::cout << "Event no guid: " << ievent->guid_no()
              << " (id=" << ievent->id() << ")" << std::endl;
    return 0;
}

int main (void)
{
    event_one eo;
    event_two et;
    event_no en;
    handler_one ho;

    std::cout << "Handler 1" << std::endl;
    ho.handle_event (&eo);
    ho.handle_event (&et);
    ho.handle_event (&en);

    std::cout << "End" << std::endl;
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...