/* Crafter®.Event Copyright (C) 2025 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3.0 as published by the Free Software Foundation; This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ export module Crafter.Event; import std; namespace Crafter { export template class Event; export template<> class Event; export template class EventListener { public: int priority; EventListener(); EventListener(EventListener&) = delete; EventListener& operator=(EventListener&) = delete; void SetEvent(Event* eventToListenTo, std::function function, int priority = 0); EventListener(Event* eventToListenTo, std::function function, int priority = 0); ~EventListener(); Event* eventToListenTo; std::function function; }; export template<> class EventListener { public: int priority; EventListener(); EventListener(EventListener&) = delete; EventListener& operator=(EventListener&) = delete; void SetEvent(Event* eventToListenTo, std::function function, int priority = 0); EventListener(Event* eventToListenTo, std::function function, int priority = 0); ~EventListener(); Event* eventToListenTo; std::function function; }; // Base implementation for Event - common functionality template class EventBase { public: #ifdef CRAFTER_TIMING std::map*, std::chrono::nanoseconds> listenerTimes; #endif protected: bool deleted = false; std::map*>> listeners; public: EventBase() = default; EventBase(EventBase&& other) : listeners(std::move(other.listeners)) { for (const auto& listenerSlice : listeners) { for (const auto& listener : listenerSlice.second) { if(listener) { listener->eventToListenTo = this; } } } } EventBase(EventBase&) = delete; EventBase& operator=(EventBase&) = delete; ~EventBase() { deleted = true; for (const auto& listenerSlice : listeners) { for (const auto& listener : listenerSlice.second) { if(listener) { listener->eventToListenTo = nullptr; } } } } void AddListener(EventListener* listener, int priority = 0) { if (listeners.contains(priority)) { listeners[priority].push_back(listener); } else { listeners[priority] = std::vector*>{ listener }; } } void RemoveListener(EventListener* listener) { auto pos = listeners.find(listener->priority); std::replace(pos->second.begin(), pos->second.end(), listener, static_cast*>(nullptr)); } #ifdef CRAFTER_TIMING // Log timing data in a clear manner void LogTiming() const { if (this->listenerTimes.empty()) { return; // No timing data to report } // Create a vector of pairs for sorting std::vector*, std::chrono::nanoseconds>> sortedTimes( this->listenerTimes.begin(), this->listenerTimes.end() ); // Sort by duration (descending order) std::sort(sortedTimes.begin(), sortedTimes.end(), [](const auto& a, const auto& b) { return a.second > b.second; }); // Print results for (const auto& pair : sortedTimes) { const auto& listener = pair.first; const auto& duration = pair.second; // Format output based on magnitude if (duration.count() >= 1000000) { // Milliseconds std::printf("Listener (priority %d): %.3f ms\n", listener->priority, static_cast(duration.count()) / 1000000.0); } else if (duration.count() >= 1000) { // Microseconds std::printf("Listener (priority %d): %.3f μs\n", listener->priority, static_cast(duration.count()) / 1000.0); } else { // Nanoseconds std::printf("Listener (priority %d): %ld ns\n", listener->priority, duration.count()); } } } #endif protected: void removeEmptyListeners() { for (auto it = listeners.begin(); it != listeners.end(); ) { if (it->second.empty()) { it = listeners.erase(it); // Erase and move to the next element } else { ++it; // Move to the next element } } } }; // Specialized Event with different Invoke signature export template<> class Event : public EventBase { public: using EventBase::EventBase; void Invoke() { #ifdef CRAFTER_TIMING this->listenerTimes.clear(); #endif std::vector keys; for (const auto& pair : this->listeners) { keys.push_back(pair.first); } for (std::int_fast32_t key : keys) { auto it = this->listeners.find(key); if (it != this->listeners.end()) { it->second.erase(std::remove(it->second.begin(), it->second.end(), static_cast*>(nullptr)), it->second.end()); auto sliceCopy = it->second; for (const auto& listener : sliceCopy) { #ifdef CRAFTER_TIMING auto start = std::chrono::high_resolution_clock::now(); #endif listener->function(); if(deleted) { return; } #ifdef CRAFTER_TIMING auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); this->listenerTimes[listener] = duration; #endif } } } removeEmptyListeners(); } }; // Generic Event with Invoke signature export template class Event : public EventBase { public: using EventBase::EventBase; void Invoke(T data) { #ifdef CRAFTER_TIMING this->listenerTimes.clear(); #endif std::vector keys; for (const auto& pair : this->listeners) { keys.push_back(pair.first); } for (std::int_fast32_t key : keys) { auto it = this->listeners.find(key); if (it != this->listeners.end()) { it->second.erase(std::remove(it->second.begin(), it->second.end(), static_cast*>(nullptr)), it->second.end()); auto sliceCopy = it->second; for (const auto& listener : sliceCopy) { #ifdef CRAFTER_TIMING auto start = std::chrono::high_resolution_clock::now(); #endif listener->function(data); if(this->deleted) { return; } #ifdef CRAFTER_TIMING auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); this->listenerTimes[listener] = duration; #endif } } } EventBase::removeEmptyListeners(); } }; // Common implementations for EventListener template void EventListener::SetEvent(Event* eventToListenTo, std::function listener, int priority) { this->priority = priority; if (this->eventToListenTo != nullptr) { eventToListenTo->RemoveListener(this); } this->eventToListenTo = eventToListenTo; eventToListenTo->AddListener(this); this->function = listener; } template EventListener::EventListener(Event* eventToListenTo, std::function listener, int priority) : priority(priority), eventToListenTo(eventToListenTo), function(listener) { eventToListenTo->AddListener(this); } template EventListener::~EventListener() { if (eventToListenTo != nullptr) { eventToListenTo->RemoveListener(this); } } template EventListener::EventListener() { eventToListenTo = nullptr; } // Specific implementations for EventListener void EventListener::SetEvent(Event* eventToListenTo, std::function listener, int priority) { this->priority = priority; if (this->eventToListenTo != nullptr) { eventToListenTo->RemoveListener(this); } this->eventToListenTo = eventToListenTo; eventToListenTo->AddListener(this); this->function = listener; } EventListener::EventListener(Event* eventToListenTo, std::function listener, int priority) : priority(priority), eventToListenTo(eventToListenTo), function(listener) { eventToListenTo->AddListener(this); } EventListener::~EventListener() { if (eventToListenTo != nullptr) { eventToListenTo->RemoveListener(this); } } EventListener::EventListener() { eventToListenTo = nullptr; } }