91 lines
2.7 KiB
C++
91 lines
2.7 KiB
C++
#pragma once
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#include <functional>
|
|
#include <typeindex>
|
|
#include <any>
|
|
#include <algorithm>
|
|
|
|
class EventTarget {
|
|
public:
|
|
template<typename T>
|
|
using Callback = std::function<void(const T &)>;
|
|
|
|
// Subscribe to an event type.
|
|
template<typename T>
|
|
uint64_t listen(Callback<T> callback) {
|
|
auto type = std::type_index(typeid(T));
|
|
auto & vec = callbacks[type];
|
|
|
|
uint64_t id = nextEventId++;
|
|
|
|
vec.emplace_back([cb = std::move(callback)](const std::any & e) {
|
|
cb(std::any_cast<const T &>(e));
|
|
});
|
|
return id;
|
|
}
|
|
|
|
// Unsubscribe a specific listener.
|
|
template<typename T>
|
|
void unlisten(uint64_t id) {
|
|
_unlisten(std::type_index(typeid(T)), id);
|
|
}
|
|
|
|
// Synchronously dispatch an event.
|
|
template<typename T>
|
|
void dispatch(const T & event) {
|
|
auto it = callbacks.find(std::type_index(typeid(T)));
|
|
if (it == callbacks.end()) return;
|
|
|
|
// Copy the list in case a callback unsubscribes during dispatch.
|
|
auto copy = it->second;
|
|
for (const auto & cb : copy) {
|
|
cb(event);
|
|
}
|
|
}
|
|
|
|
// Should be called on a main loop to dispatch queued events.
|
|
void processQueue() {
|
|
for (auto & [anyEvent, copy] : queued) {
|
|
auto it = callbacks.find(anyEvent.type());
|
|
if (it != callbacks.end()) {
|
|
for (const auto & cb : copy) {
|
|
cb(anyEvent);
|
|
}
|
|
}
|
|
}
|
|
queued.clear();
|
|
}
|
|
|
|
// Queue an event to be dispatched on the next call to `processQueue`.
|
|
template<typename T>
|
|
void queue(const T & event) {
|
|
auto type = std::type_index(typeid(T));
|
|
auto it = callbacks.find(type);
|
|
if (it == callbacks.end()) return;
|
|
queued.emplace_back(std::any(event), it->second);
|
|
}
|
|
|
|
private:
|
|
using AnyCallback = std::function<void(const std::any &)>;
|
|
using CallbackWithId = std::pair<AnyCallback, uint64_t>;
|
|
|
|
std::unordered_map<std::type_index, std::vector<AnyCallback>> callbacks {};
|
|
std::vector<std::pair<std::any, std::vector<AnyCallback>>> queued {};
|
|
|
|
uint64_t nextEventId = 0;
|
|
|
|
void _unlisten(std::type_index type, uint64_t id) {
|
|
// auto it = callbacks.find(type);
|
|
// if (it == callbacks.end()) return;
|
|
//
|
|
// auto & vec = it->second;
|
|
// auto erase_it = std::find_if(vec.begin(), vec.end(),
|
|
// [id](const auto& p) { return p.second == id; });
|
|
//
|
|
// if (erase_it != vec.end()) {
|
|
// *erase_it = std::move(vec.back());
|
|
// vec.pop_back();
|
|
// }
|
|
}
|
|
};
|