10#include <unordered_map>
14 template <
typename... ArgTypes>
class Conduit {
27 : state(std::move(other.state)), id(other.id) {
33 state = std::move(other.state);
41 auto locked = state.lock();
42 if (!locked ||
id == 0) {
45 std::lock_guard<std::mutex> guard(locked->mutex);
46 return locked->index.find(
id) != locked->index.end();
50 auto locked = state.lock();
51 if (!locked ||
id == 0) {
54 std::lock_guard<std::mutex> guard(locked->mutex);
55 auto it = locked->index.find(
id);
56 if (it == locked->index.end()) {
60 locked->entries.erase(it->second);
61 locked->index.erase(it);
67 auto locked = state.lock();
68 if (!locked ||
id == 0) {
71 std::lock_guard<std::mutex> guard(locked->mutex);
72 auto it = locked->index.find(
id);
73 if (it == locked->index.end()) {
77 it->second->paused =
true;
82 auto locked = state.lock();
83 if (!locked ||
id == 0) {
86 std::lock_guard<std::mutex> guard(locked->mutex);
87 auto it = locked->index.find(
id);
88 if (it == locked->index.end()) {
92 it->second->paused =
false;
97 auto locked = state.lock();
98 if (!locked ||
id == 0) {
101 std::lock_guard<std::mutex> guard(locked->mutex);
102 auto it = locked->index.find(
id);
103 if (it == locked->index.end()) {
106 return it->second->paused;
111 Tap(std::weak_ptr<State> subscription_state, std::uint64_t subscription_id)
112 : state(std::move(subscription_state)), id(subscription_id) { }
114 std::weak_ptr<State> state;
115 std::uint64_t
id = 0;
121 std::lock_guard<std::mutex> guard(state->mutex);
122 const std::uint64_t
id = state->next_id++;
123 auto it = state->entries.insert(state->entries.end(), Entry{ id, std::move(callback), false });
124 state->index.emplace(
id, it);
125 return Tap(state,
id);
132 std::lock_guard<std::mutex> guard(state->mutex);
133 auto it = state->index.find(
id);
134 if (it == state->index.end()) {
137 state->entries.erase(it->second);
138 state->index.erase(it);
142 virtual bool Pause(std::uint64_t
id) {
146 std::lock_guard<std::mutex> guard(state->mutex);
147 auto it = state->index.find(
id);
148 if (it == state->index.end()) {
151 it->second->paused =
true;
159 std::lock_guard<std::mutex> guard(state->mutex);
160 auto it = state->index.find(
id);
161 if (it == state->index.end()) {
164 it->second->paused =
false;
172 std::lock_guard<std::mutex> guard(state->mutex);
173 auto it = state->index.find(
id);
174 if (it == state->index.end()) {
177 return it->second->paused;
180 virtual void Trigger(ArgTypes... args)
const {
181 std::vector<Callback_t> callbacks;
183 std::lock_guard<std::mutex> guard(state->mutex);
184 callbacks.reserve(state->entries.size());
185 for (
const auto& entry : state->entries) {
187 callbacks.push_back(entry.callback);
191 for (
auto& callback : callbacks) {
208 std::list<Entry> entries;
209 std::unordered_map<std::uint64_t, typename std::list<Entry>::iterator> index;
210 std::uint64_t next_id = 1;
213 std::shared_ptr<State> state = std::make_shared<State>();
225 template <
typename TapT>
void Add(TapT tap) {
226 if (!tap.IsActive()) {
229 std::lock_guard<std::mutex> guard(mutex);
230 entries.emplace_back(std::make_unique<TapEntry<TapT>>(std::move(tap)));
239 std::vector<std::unique_ptr<TapEntryBase>> to_run;
241 std::lock_guard<std::mutex> guard(mutex);
242 to_run.swap(entries);
244 for (
auto& tap : to_run) {
250 std::lock_guard<std::mutex> guard(mutex);
251 for (
auto& tap : entries) {
257 std::lock_guard<std::mutex> guard(mutex);
258 for (
auto& tap : entries) {
264 std::lock_guard<std::mutex> guard(mutex);
269 std::lock_guard<std::mutex> guard(mutex);
270 return entries.size();
275 struct TapEntryBase {
276 virtual ~TapEntryBase() =
default;
277 virtual void Detach() = 0;
278 virtual void Pause() = 0;
279 virtual void Resume() = 0;
282 template <
typename TapT>
struct TapEntry : TapEntryBase {
283 explicit TapEntry(TapT&& tap_value)
284 : tap(std::move(tap_value)) { }
286 void Detach()
override { tap.Detach(); }
287 void Pause()
override { tap.Pause(); }
288 void Resume()
override { tap.Resume(); }
293 mutable std::mutex mutex;
294 std::vector<std::unique_ptr<TapEntryBase>> entries;
Tap & operator=(const Tap &)=delete
Tap & operator=(Tap &&other) noexcept
bool Detach()
Manually unsubscribe from the event.
Tap(Tap &&other) noexcept
bool IsActive() const
Check if this subscription is still active.
void operator()(ArgTypes... args) const
Convenience operator for triggering the event.
std::function< void(ArgTypes...)> Callback_t
Type alias for event callback function.
virtual bool Pause(std::uint64_t id)
virtual bool Detach(std::uint64_t id)
Detach by subscription ID.
Tap operator%(Callback_t callback)
virtual bool IsPaused(std::uint64_t id) const
virtual ~Conduit()=default
virtual Tap Attach(Callback_t callback)
Register a callback for this event.
virtual void Trigger(ArgTypes... args) const
Trigger the event, invoking all registered callbacks.
virtual bool Resume(std::uint64_t id)
TapScope(TapScope &&)=delete
TapScope & operator=(TapScope &&)=delete
std::size_t Size() const
Get the number of subscriptions in this group.
void Add(TapT tap)
Add a subscription to this group.
TapScope(const TapScope &)=delete
void DetachAll()
Detach all contained subscriptions.
TapScope & operator=(const TapScope &)=delete
TapScope & operator<<(TapT tap)
Fluent API for adding subscriptions.
void Clear()
Clear all subscriptions without unsubscribing.