//+------------------------------------------------------------------+ //| EventBus.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "../Interfaces/Listerners/IStructEventListener.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class EventBus { private: IEventListener* m_listeners[]; // dynamic array of listeners bool m_isNotifying; // flag to prevent modification during iteration int m_pendingRemovals; // for deferred removal // Helper to remove at index (shifts left) void RemoveAt(int index) { int size = ArraySize(m_listeners); for(int i = index; i < size - 1; i++) m_listeners[i] = m_listeners[i + 1]; ArrayResize(m_listeners, size - 1); } // Clean up null pointers from array (during iteration or after) void CleanupNullListeners() { if(m_isNotifying) return; // defer until after publish for(int i = ArraySize(m_listeners) - 1; i >= 0; i--) { if(CheckPointer(m_listeners[i]) == POINTER_INVALID) RemoveAt(i); } } public: EventBus() : m_isNotifying(false) {} ~EventBus() { UnsubscribeAll(); } // Subscribe – adds listener if not already subscribed void Subscribe(IEventListener *listener) { if(listener == NULL) return; if(CheckPointer(listener) == POINTER_INVALID) return; // Avoid duplicate subscriptions for(int i = 0; i < ArraySize(m_listeners); i++) { if(m_listeners[i] == listener) return; } int size = ArraySize(m_listeners); ArrayResize(m_listeners, size + 1); m_listeners[size] = listener; } // Unsubscribe by pointer void Unsubscribe(IEventListener *listener) { if(listener == NULL) return; if(m_isNotifying) { // Defer removal – mark as NULL (will be cleaned after publish) for(int i = 0; i < ArraySize(m_listeners); i++) { if(m_listeners[i] == listener) { m_listeners[i] = NULL; m_pendingRemovals++; break; } } } else { for(int i = 0; i < ArraySize(m_listeners); i++) { if(m_listeners[i] == listener) { RemoveAt(i); break; } } } } // Unsubscribe all listeners void UnsubscribeAll() { if(m_isNotifying) return; // prevent modification during iteration ArrayResize(m_listeners, 0); m_pendingRemovals = 0; } // Publish an event (takes ownership – caller must allocate with new) void Publish(IEvent *event) { if(event == NULL) return; m_isNotifying = true; int size = ArraySize(m_listeners); for(int i = 0; i < size; i++) { IEventListener* listener = m_listeners[i]; if(CheckPointer(listener) == POINTER_INVALID) continue; // skip null entries (will be cleaned later) listener.OnEvent(event); } m_isNotifying = false; // Clean up any null listeners that were marked during iteration if(m_pendingRemovals > 0) { CleanupNullListeners(); m_pendingRemovals = 0; } delete event; // event bus takes ownership } // Get number of active listeners int GetListenerCount() const { int count = 0; for(int i = 0; i < ArraySize(m_listeners); i++) { if(CheckPointer(m_listeners[i]) != POINTER_INVALID) count++; } return count; } }; //+-------------Stores-----------------------------------------------------+ /*class EventBus { private: IEvent *events[]; IEventListener *listeners[]; public: void subscribe(IEventListener *l) { int size = ArraySize(listeners); ArrayResize(listeners, size + 1); listeners[size] = l; } void publish(IEvent *e) { // store event (optional) int size = ArraySize(events); ArrayResize(events, size + 1); events[size] = e; // broadcast for(int i = 0; i < ArraySize(listeners); i++) { listeners[i].onEvent(e); } } };*/ //+------------------------------------------------------------------+