I try to achieve an asynchronous event loop which can store events in a queue and dispatches them later to different subscribers. All events are derived from one base, also all subscribers are derived from their own base class. Events should hold different types of values. Subscribers shall forward them to instances of other classes. I can only use std library.
I have problems to dispatch events of different types to the proper methods of the subscriber sub class. If I store Event Base in the queue, I lose type information of the derived events and cannot call the proper methods of the subscriber. I also do not want to use If then else or switch on type Info.
I tried using templates and create for every event its own dispatch method in the subscriber class or std::function to wrap the method call in a lambda function.
My approach seams utterly complicated and slow. I cannot forward the events to the classes managed by the subscriber. I always get error no matching function call. I understand why but have no idea how to solve my problem.
Usage:
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();
What I tried so far:
#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;
}
Subscribers:
//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);*
}
another_classinterface in queue so the call toreactis handled through thevtableand thus not losing type data. - Manuelanother_class_ptrcan change to another instance and make the dispatch call illegal. How would I achieve your suggestion? I cannot overload virtual methods inyet_another_classor use templated virtual methods . If I use templatedreactmethods inanother_class, lambdas always call Base class methods, not derived ones. I'm a bit lost here. - T.bur