14using ListenerId = uint32_t;
22 virtual ~IBus() =
default;
24 IBus(
const IBus& other) =
delete;
25 IBus& operator=(
const IBus& other) =
delete;
27 IBus(IBus && other) =
delete;
28 IBus& operator=(IBus && other) =
delete;
60template<
typename EventT>
61class Bus :
public IBus {
64 ~Bus()
override =
default;
66 Bus(
const Bus& other) =
delete;
67 Bus& operator=(
const Bus& other) =
delete;
69 Bus(Bus && other) =
delete;
70 Bus& operator=(Bus && other) =
delete;
83 ListenerId
subscribe(std::function<
void(
const EventT&)> cb) {
84 const ListenerId
id = m_nextId++;
86 if (m_flushDepth > 0) m_pending.push_back({id, std::move(cb)});
87 else m_listeners.push_back({id, std::move(cb)});
104 for (
auto it = m_listeners.begin(); it != m_listeners.end(); ++it) {
105 if (it->id !=
id)
continue;
107 if (m_flushDepth != 0) it->alive =
false;
108 else m_listeners.erase(it);
111 for (
auto it = m_pending.begin(); it != m_pending.end(); ++it) {
112 if (it->id !=
id)
continue;
125 void emit(
const EventT& event) {
126 DispatchScope scope(*
this);
127 const size_t n = m_listeners.size();
128 for (
size_t i = 0; i < n; ++i) {
129 if (m_listeners[i].alive) m_listeners[i].cb(event);
139 m_queue.push_back(std::move(event));
149 m_dispatch.swap(m_queue);
158 if (m_dispatch.empty())
return;
160 DispatchScope scope(*
this);
161 const size_t n = m_listeners.size();
162 for (
auto& e : m_dispatch) {
163 for (
size_t i = 0; i < n; ++i) {
164 if (m_listeners[i].alive) m_listeners[i].cb(e);
175 for (
const Entry& e : m_listeners) {
176 if (e.alive)
return true;
178 return !m_pending.empty();
184 std::function<void(
const EventT&)> cb;
194 class DispatchScope {
196 explicit DispatchScope(Bus& bus) : m_bus(bus) { ++m_bus.m_flushDepth; }
198 --m_bus.m_flushDepth;
199 m_bus.admitPending();
202 DispatchScope(
const DispatchScope& other) =
delete;
203 DispatchScope& operator=(
const DispatchScope& other) =
delete;
205 DispatchScope(DispatchScope && other) =
delete;
206 DispatchScope& operator=(DispatchScope && other) =
delete;
218 void admitPending() {
219 if (m_flushDepth != 0)
return;
221 const auto dead = [](
const Entry& e) {
return !e.alive; };
223 std::remove_if(m_listeners.begin(), m_listeners.end(), dead),
227 if (m_pending.empty())
return;
228 for (Entry& entry : m_pending) m_listeners.push_back(std::move(entry));
233 std::vector<Entry> m_listeners;
234 std::vector<Entry> m_pending;
235 std::vector<EventT> m_queue;
236 std::vector<EventT> m_dispatch;
237 ListenerId m_nextId = 1;
238 int m_flushDepth = 0;
bool hasListeners() const override
Whether any live listener remains, mid-dispatch removals aside.
Definition bus.h:174
void deliver() override
Deliver the batch takeQueue set aside to every current listener.
Definition bus.h:157
void emit(const EventT &event)
Dispatch event to every current listener synchronously.
Definition bus.h:125
void takeQueue() override
Swap the queue aside so what a listener enqueues cannot join it.
Definition bus.h:147
ListenerId subscribe(std::function< void(const EventT &)> cb)
Append a listener and return its new id.
Definition bus.h:83
bool remove(ListenerId id)
Erase the listener with id.
Definition bus.h:103
void enqueue(EventT event)
Buffer event for delivery on the next flush().
Definition bus.h:138
virtual void takeQueue()=0
Set this bus's queued events aside, ready to be delivered.
virtual bool hasListeners() const =0
Whether anything is still listening on this bus.
virtual void deliver()=0
Deliver what takeQueue set aside, to this bus's listeners.