#pragma once #include #include #include #include #include #include class EventBus; class SubscriptionHandle { public: SubscriptionHandle() = default; ~SubscriptionHandle() { unsubscribe(); } SubscriptionHandle(SubscriptionHandle&& other) noexcept : typeId_(other.typeId_), handlerId_(other.handlerId_), bus_(other.bus_) { other.bus_ = nullptr; } SubscriptionHandle& operator=(SubscriptionHandle&& other) noexcept { if (this != &other) { unsubscribe(); typeId_ = other.typeId_; handlerId_ = other.handlerId_; bus_ = other.bus_; other.bus_ = nullptr; } return *this; } SubscriptionHandle(const SubscriptionHandle&) = delete; SubscriptionHandle& operator=(const SubscriptionHandle&) = delete; void unsubscribe(); private: friend class EventBus; using TypeId = const void*; SubscriptionHandle(TypeId typeId, uint32_t handlerId, EventBus* bus) : typeId_(typeId), handlerId_(handlerId), bus_(bus) {} TypeId typeId_ = nullptr; uint32_t handlerId_ = 0; EventBus* bus_ = nullptr; }; class EventBus { public: using TypeId = const void*; template static TypeId typeId() { static const char id = 0; return &id; } static EventBus& instance() { static EventBus bus; return bus; } template SubscriptionHandle subscribe(std::function callback) { xSemaphoreTake(mutex_, portMAX_DELAY); auto id = typeId(); uint32_t hid = nextHandlerId_++; handlers_[id].push_back( {hid, [cb = std::move(callback)](const void* data) { cb(*static_cast(data)); }}); xSemaphoreGive(mutex_); return SubscriptionHandle(id, hid, this); } template void publish(const T& event) { xSemaphoreTake(mutex_, portMAX_DELAY); auto id = typeId(); auto& entry = cache_[id]; if (entry.data) { *static_cast(entry.data) = event; } else { entry.data = new T(event); entry.deleter = [](void* p) { delete static_cast(p); }; } auto it = handlers_.find(id); if (it == handlers_.end()) { xSemaphoreGive(mutex_); return; } auto snapshot = it->second; xSemaphoreGive(mutex_); for (auto& handler : snapshot) { handler.callback(&event); } } template bool has() { xSemaphoreTake(mutex_, portMAX_DELAY); auto it = cache_.find(typeId()); bool result = it != cache_.end() && it->second.data != nullptr; xSemaphoreGive(mutex_); return result; } template T peek() { xSemaphoreTake(mutex_, portMAX_DELAY); auto it = cache_.find(typeId()); T result = (it != cache_.end() && it->second.data) ? *static_cast(it->second.data) : T {}; xSemaphoreGive(mutex_); return result; } private: friend class SubscriptionHandle; EventBus() { mutex_ = xSemaphoreCreateMutex(); } ~EventBus() { for (auto& [id, entry] : cache_) { if (entry.deleter) entry.deleter(entry.data); } vSemaphoreDelete(mutex_); } EventBus(const EventBus&) = delete; EventBus& operator=(const EventBus&) = delete; void removeHandler(TypeId typeId, uint32_t handlerId) { xSemaphoreTake(mutex_, portMAX_DELAY); auto it = handlers_.find(typeId); if (it != handlers_.end()) { it->second.remove_if([handlerId](const Handler& h) { return h.id == handlerId; }); } xSemaphoreGive(mutex_); } struct Handler { uint32_t id; std::function callback; }; struct CacheEntry { void* data = nullptr; void (*deleter)(void*) = nullptr; }; SemaphoreHandle_t mutex_; uint32_t nextHandlerId_ = 1; std::map> handlers_; std::map cache_; }; inline void SubscriptionHandle::unsubscribe() { if (bus_) { bus_->removeHandler(typeId_, handlerId_); bus_ = nullptr; } }