mql5/Experts/Motivated/Systems/EventBus.mqh
younglubah-gmail 0afee9e947
2026-06-10 23:03:10 +02:00

168 lines
4.8 KiB
MQL5

//+------------------------------------------------------------------+
//| 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);
}
}
};*/
//+------------------------------------------------------------------+