Below you can see my implementation of a simple event dispatcher. I would like to get some feedback or improvement tips regarding the design and maybe an idea how to get rid of the static_cast. Thank you very much.
Classes
Event.h
#pragma once
#include <cstdint>
enum class EventType : uint8_t {
ButtonPress,
FactoryReset
};
class Event {
public:
Event() = default;
explicit Event(EventType type) : mType(type) {}
virtual ~Event() = default;
inline const EventType Type() const { return this->mType; };
protected:
EventType mType;
};
ButtonPressEvent.h
#pragma once
#include "Event.h"
class ButtonPressEvent final : public Event {
public:
ButtonPressEvent(uint8_t pin) : Event(EventType::ButtonPress), mPin(pin) {};
uint8_t mPin;
};
FactoryResetEvent.h
#pragma once
#include "Event.h"
class FactoryResetEvent final : public Event {
public:
FactoryResetEvent() : Event(EventType::FactoryReset) {};
};
Subscriber.h
#pragma once
#include <functional>
#include <map>
#include <memory>
#include "Event.h"
using EventHandlerType = std::function<void(std::unique_ptr<Event>&)>;
class Subscriber {
public:
virtual ~Subscriber() = default;
virtual std::map<EventType, EventHandlerType> GetSubscribedEvents() = 0;
};
Dispatcher.h
#pragma once
#include <map>
#include <memory>
#include <vector>
#include "Event.h"
#include "Subscriber.h"
class Dispatcher {
public:
Dispatcher();
void Listen(EventType type, const EventHandlerType &funct);
void Subscribe(Subscriber *subscriber);
void Post(std::unique_ptr<Event> &ptr);
void Process();
private:
std::map<EventType, std::vector<EventHandlerType>> mObservers;
std::vector<std::unique_ptr<Event>> mEvents;
};
Dispatcher.cpp
#include "Dispatcher.h"
void Dispatcher::Listen(EventType type, const EventHandlerType &funct) {
this->mObservers[type].push_back(funct);
}
void Dispatcher::Subscribe(Subscriber *subscriber) {
for (auto&& event : subscriber->GetSubscribedEvents()) {
this->Listen(event.first, event.second);
}
}
void Dispatcher::Post(std::unique_ptr<Event> &ptr) {
this->mEvents.push_back(std::move(ptr));
}
void Dispatcher::Process() {
if (this->mEvents.empty()) {
return;
}
for (auto&& event : this->mEvents) {
// call listener functions
for (auto&& observer : this->mObservers.at(event->Type())) {
observer(event);
}
}
this->mEvents.clear();
}
Usage
App.h
#pragma once
#include "Subscriber.h"
class App : public Subscriber {
public:
std::map<EventType, EventHandlerType> GetSubscribedEvents();
void OnButtonPressEvent(std::unique_ptr<Event> &event);
};
App.cpp
#include <functional>
#include <stdio.h>
#include "App.h"
#include "Event.h"
#include "ButtonPressEvent.h"
std::map<EventType, EventHandlerType> App::GetSubscribedEvents() {
return {
{ EventType::ButtonPress, std::bind(&App::OnButtonPressEvent, this, std::placeholders::_1) }
};
}
void App::OnButtonPressEvent(std::unique_ptr<Event> &event) {
ButtonPressEvent *b = static_cast<ButtonPressEvent*>(event.get());
printf("Button pin %d pressed", b->mPin);
}
main.cpp
#include "Dispatcher.h"
#include "ButtonPressEvent.h"
#include "App.h"
void main(void) {
App app;
Dispatcher dispatcher;
dispatcher.Subscribe(&app);
// simulate event
std::unique_ptr<Event> ptr(new ButtonPressEvent(11));
dispatcher.Post(ptr);
while (true) {
dispatcher.Process();
}
}