eventbus
event_bus.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <any>
4 #include <atomic>
5 #include <functional>
6 #include <mutex>
7 #include <shared_mutex>
8 #include <thread>
9 #include <typeindex>
10 #include <unordered_map>
11 #include <utility>
12 
13 #include "detail/function_traits.hpp"
14 
15 namespace dp {
16  class event_bus;
17 
26  const void* handle_{nullptr};
27  dp::event_bus* event_bus_{nullptr};
28 
29  public:
35 
39  [[nodiscard]] const void* handle() const;
40 
44  void unregister() noexcept;
45 
46  protected:
47  handler_registration(const void* handle, dp::event_bus* bus);
48  friend class event_bus;
49  };
50 
54  class event_bus {
55  public:
56  event_bus() = default;
57 
66  template <typename EventType, typename EventHandler,
67  typename = std::enable_if_t<std::is_invocable_v<EventHandler> ||
68  std::is_invocable_v<EventHandler, EventType>>>
69  [[nodiscard]] handler_registration register_handler(EventHandler&& handler) {
70  using traits = detail::function_traits<EventHandler>;
71  const auto type_idx = std::type_index(typeid(EventType));
72  const void* handle;
73  // check if the function takes any arguments.
74  if constexpr (traits::arity == 0) {
75  safe_unique_registrations_access([&]() {
76  auto it = handler_registrations_.emplace(
77  type_idx,
78  [handler = std::forward<EventHandler>(handler)](auto) { handler(); });
79 
80  handle = static_cast<const void*>(&(it->second));
81  });
82  } else {
83  safe_unique_registrations_access([&]() {
84  auto it = handler_registrations_.emplace(
85  type_idx, [func = std::forward<EventHandler>(handler)](auto value) {
86  func(std::any_cast<EventType>(value));
87  });
88 
89  handle = static_cast<const void*>(&(it->second));
90  });
91  }
92  return {handle, this};
93  }
94 
104  template <typename EventType, typename ClassType, typename MemberFunction>
105  [[nodiscard]] handler_registration register_handler(ClassType* class_instance,
106  MemberFunction&& function) noexcept {
107  using traits = detail::function_traits<MemberFunction>;
108  static_assert(std::is_same_v<ClassType, std::decay_t<typename traits::owner_type>>,
109  "Member function pointer must match instance type.");
110 
111  const auto type_idx = std::type_index(typeid(EventType));
112  const void* handle;
113 
114  if constexpr (traits::arity == 0) {
115  safe_unique_registrations_access([&]() {
116  auto it = handler_registrations_.emplace(
117  type_idx,
118  [class_instance, function](auto) { (class_instance->*function)(); });
119 
120  handle = static_cast<const void*>(&(it->second));
121  });
122  } else {
123  safe_unique_registrations_access([&]() {
124  auto it = handler_registrations_.emplace(
125  type_idx, [class_instance, function](auto value) {
126  (class_instance->*function)(std::any_cast<EventType>(value));
127  });
128 
129  handle = static_cast<const void*>(&(it->second));
130  });
131  }
132  return {handle, this};
133  }
134 
140  template <typename EventType, typename = std::enable_if_t<!std::is_pointer_v<EventType>>>
141  void fire_event(EventType&& evt) noexcept {
142  safe_shared_registrations_access([this, local_event = std::forward<EventType>(evt)]() {
143  // only call the functions we need to
144  for (auto [begin_evt_id, end_evt_id] =
145  handler_registrations_.equal_range(std::type_index(typeid(EventType)));
146  begin_evt_id != end_evt_id; ++begin_evt_id) {
147  begin_evt_id->second(local_event);
148  }
149  });
150  }
151 
157  bool remove_handler(const handler_registration& registration) noexcept {
158  if (!registration.handle()) {
159  return false;
160  }
161 
162  auto result = false;
163  safe_unique_registrations_access([this, &result, &registration]() {
164  for (auto it = handler_registrations_.begin(); it != handler_registrations_.end();
165  ++it) {
166  if (static_cast<const void*>(&(it->second)) == registration.handle()) {
167  handler_registrations_.erase(it);
168  result = true;
169  break;
170  }
171  }
172  });
173  return result;
174  }
175 
179  void remove_handlers() noexcept {
180  safe_unique_registrations_access([this]() { handler_registrations_.clear(); });
181  }
182 
187  [[nodiscard]] std::size_t handler_count() noexcept {
188  std::size_t count{};
189  safe_shared_registrations_access(
190  [this, &count]() { count = handler_registrations_.size(); });
191  return count;
192  }
193 
194  private:
195  using mutex_type = std::shared_mutex;
196  mutable mutex_type registration_mutex_;
197  std::unordered_multimap<std::type_index, std::function<void(std::any)>>
198  handler_registrations_;
199 
200  template <typename Callable>
201  void safe_shared_registrations_access(Callable&& callable) {
202  try {
203  std::shared_lock<mutex_type> lock(registration_mutex_);
204  callable();
205  } catch (std::system_error&) {
206  }
207  }
208  template <typename Callable>
209  void safe_unique_registrations_access(Callable&& callable) {
210  try {
211  // if this fails, an exception may be thrown.
212  std::unique_lock<mutex_type> lock(registration_mutex_);
213  callable();
214  } catch (std::system_error&) {
215  // do nothing
216  }
217  }
218  };
219 
220  inline const void* handler_registration::handle() const { return handle_; }
221 
222  inline void handler_registration::unregister() noexcept {
223  if (event_bus_ && handle_) {
224  event_bus_->remove_handler(*this);
225  handle_ = nullptr;
226  }
227  }
228 
230  : handle_(handle), event_bus_(bus) {}
231 
233  : handle_(std::exchange(other.handle_, nullptr)),
234  event_bus_(std::exchange(other.event_bus_, nullptr)) {}
235 
237  handler_registration&& other) noexcept {
238  handle_ = std::exchange(other.handle_, nullptr);
239  event_bus_ = std::exchange(other.event_bus_, nullptr);
240  return *this;
241  }
242 
244 } // namespace dp
A central event handler class that connects event handlers with the events.
Definition: event_bus.hpp:54
void fire_event(EventType &&evt) noexcept
Fire an event to notify event handlers.
Definition: event_bus.hpp:141
event_bus()=default
void remove_handlers() noexcept
Remove all handlers from event bus.
Definition: event_bus.hpp:179
handler_registration register_handler(EventHandler &&handler)
Register an event handler for a given event type.
Definition: event_bus.hpp:69
bool remove_handler(const handler_registration &registration) noexcept
Remove a given handler from the event bus.
Definition: event_bus.hpp:157
std::size_t handler_count() noexcept
Get the number of handlers registered with the event bus.
Definition: event_bus.hpp:187
handler_registration register_handler(ClassType *class_instance, MemberFunction &&function) noexcept
Register an event handler for a given event type.
Definition: event_bus.hpp:105
A registration handle for a particular handler of an event type.
Definition: event_bus.hpp:25
const void * handle() const
Pointer to the underlying handle.
Definition: event_bus.hpp:220
handler_registration(const handler_registration &other)=delete
~handler_registration()
Definition: event_bus.hpp:243
handler_registration & operator=(const handler_registration &other)=delete
void unregister() noexcept
Unregister this handler from the event bus.
Definition: event_bus.hpp:222
Definition: event_bus.hpp:15