2026-06-09 05:44:15 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| EventBus.mqh |
|
| | | //| Copyright 2025, MetaQuotes Ltd. |
|
| | | //| https://www.mql5.com |
|
| | | //+------------------------------------------------------------------+
|
| | |
|
| | | #include "../Interfaces/Listerners/IStructEventListener.mqh"
|
| | |
|
2026-06-10 23:03:10 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | class EventBus
|
| | | {
|
2026-06-09 05:44:15 +02:00 | | | private:
|
2026-06-10 23:03:10 +02:00 | | | 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);
|
| | | }
|
| | | }
|
2026-06-09 05:44:15 +02:00 | | |
|
| | | public:
|
2026-06-10 23:03:10 +02:00 | | | EventBus() : m_isNotifying(false) {}
|
| | | ~EventBus() { UnsubscribeAll(); }
|
2026-06-09 05:44:15 +02:00 | | |
|
2026-06-10 23:03:10 +02:00 | | | // Subscribe – adds listener if not already subscribed
|
| | | void Subscribe(IEventListener *listener)
|
| | | {
|
| | | if(listener == NULL) return;
|
| | | if(CheckPointer(listener) == POINTER_INVALID) return;
|
2026-06-09 05:44:15 +02:00 | | |
|
2026-06-10 23:03:10 +02:00 | | | // Avoid duplicate subscriptions
|
| | | for(int i = 0; i < ArraySize(m_listeners); i++)
|
| | | {
|
| | | if(m_listeners[i] == listener)
|
| | | return;
|
| | | }
|
2026-06-09 05:44:15 +02:00 | | |
|
2026-06-10 23:03:10 +02:00 | | | 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;
|
| | | }
|
| | | };
|
2026-06-09 05:44:15 +02:00 | | | //+-------------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);
|
| | | }
|
| | | }
|
2026-06-10 23:03:10 +02:00 | | | };*/
|
| | | //+------------------------------------------------------------------+
|