/* 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: 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; virtual ~EventBase() { 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 }; // Specialized Event with different Invoke signature export template<> class Event : public EventBase { public: using EventBase::EventBase; void Invoke() { #ifdef CRAFTER_TIMING // Clear previous timing data this->listenerTimes.clear(); this->listeners.erase(std::remove(this->listeners.begin(), this->listeners.end(), static_cast>(nullptr)), this->listeners.end()); for (const auto& listenerSlice : this->listeners) { for (const auto& listener : listenerSlice.second) { auto start = std::chrono::high_resolution_clock::now(); listener->function(); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); this->listenerTimes[listener] = duration; } } #else this->listeners.erase(std::remove(this->listeners.begin(), this->listeners.end(), static_cast>(nullptr)), this->listeners.end()); for (const auto& listenerSlice : this->listeners) { for (const auto& listener : listenerSlice.second) { listener->function(); } } #endif } }; // Generic Event with Invoke signature export template class Event : public EventBase { public: using EventBase::EventBase; void Invoke(T data) { #ifdef CRAFTER_TIMING // Clear previous timing data this->listenerTimes.clear(); this->listeners.erase(std::remove(this->listeners.begin(), this->listeners.end(), static_cast>(nullptr)), this->listeners.end()); for (const auto& listenerSlice : this->listeners) { for (const auto& listener : listenerSlice.second) { auto start = std::chrono::high_resolution_clock::now(); listener->function(data); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); this->listenerTimes[listener] = duration; } } #else this->listeners.erase(std::remove(this->listeners.begin(), this->listeners.end(), static_cast>(nullptr)), this->listeners.end()); for (const auto& listenerSlice : this->listeners) { for (const auto& listener : listenerSlice.second) { listener->function(data); } } #endif } }; // 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; } }