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_class
interface in queue so the call toreact
is handled through thevtable
and thus not losing type data. – Manuelanother_class_ptr
can change to another instance and make the dispatch call illegal. How would I achieve your suggestion? I cannot overload virtual methods inyet_another_class
or use templated virtual methods . If I use templatedreact
methods inanother_class
, lambdas always call Base class methods, not derived ones. I'm a bit lost here. – T.bur