#pragma once #include #include #include #include #include #include class EventTarget { public: template using Callback = std::function; // Subscribe to an event type. template uint64_t listen(Callback 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(e)); }); return id; } // Unsubscribe a specific listener. template void unlisten(uint64_t id) { _unlisten(std::type_index(typeid(T)), id); } // Synchronously dispatch an event. template 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 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; using CallbackWithId = std::pair; std::unordered_map> callbacks {}; std::vector>> 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(); // } } };