@@ -32,20 +32,20 @@ namespace dp {
3232 handler_registration (handler_registration&& other) noexcept ;
3333 handler_registration& operator =(const handler_registration& other) = delete ;
3434 handler_registration& operator =(handler_registration&& other) noexcept ;
35- ~handler_registration ();
35+ ~handler_registration () noexcept ;
3636
3737 /* *
3838 * @brief Pointer to the underlying handle.
3939 */
40- [[nodiscard]] const void * handle () const ;
40+ [[nodiscard]] const void * handle () const noexcept ;
4141
4242 /* *
4343 * @brief Unregister this handler from the event bus.
4444 */
4545 void unregister () noexcept ;
4646
4747 protected:
48- handler_registration (const void * handle, dp::event_bus* bus);
48+ handler_registration (const void * handle, dp::event_bus* bus) noexcept ;
4949 friend class event_bus ;
5050 };
5151
@@ -54,43 +54,37 @@ namespace dp {
5454 */
5555 class event_bus {
5656 public:
57- event_bus () = default ;
57+ /* *
58+ * @brief Register an event handler for a given event type.
59+ * @tparam EventHandler The invocable event handler type.
60+ * @param handler A callable handler of the event type. This invocation is designed for when
61+ * `handler` takes the EventType as an argument.
62+ * @return A handler_registration instance for the given handler.
63+ */
64+ template <typename EventHandler>
65+ [[nodiscard]] auto register_handler (EventHandler&& handler) noexcept {
66+ using EventType = typename detail::function_traits<EventHandler>::template arg<0 >::type;
67+
68+ static_assert (std::is_invocable_v<EventHandler, EventType>,
69+ " EventHandler must be invocable with EventType as an argument." );
70+
71+ return register_handler_impl<EventType>(std::forward<EventHandler>(handler));
72+ }
5873
5974 /* *
6075 * @brief Register an event handler for a given event type.
6176 * @tparam EventType The event type
6277 * @tparam EventHandler The invocable event handler type.
63- * @param handler A callable handler of the event type. Can accept the event as param or
64- * take no params .
78+ * @param handler A callable handler of the event type. This invocation is used for when
79+ * `handler` takes no parameters but wants to be fired when EventType is fired .
6580 * @return A handler_registration instance for the given handler.
6681 */
67- template <typename EventType, typename EventHandler,
68- typename = std::enable_if_t <std::is_invocable_v<EventHandler> ||
69- std::is_invocable_v<EventHandler, EventType>>>
70- [[nodiscard]] handler_registration register_handler (EventHandler&& handler) {
71- using traits = detail::function_traits<EventHandler>;
72- const auto type_idx = std::type_index (typeid (EventType));
73- const void * handle;
74- // check if the function takes any arguments.
75- if constexpr (traits::arity == 0 ) {
76- safe_unique_registrations_access ([&]() {
77- auto it = handler_registrations_.emplace (
78- type_idx,
79- [handler = std::forward<EventHandler>(handler)](auto ) { handler (); });
82+ template <typename EventType, typename EventHandler>
83+ [[nodiscard]] handler_registration register_handler (EventHandler&& handler) noexcept {
84+ static_assert (std::is_invocable_v<EventHandler>,
85+ " EventHandler must be invocable with no arguments." );
8086
81- handle = static_cast <const void *>(&(it->second ));
82- });
83- } else {
84- safe_unique_registrations_access ([&]() {
85- auto it = handler_registrations_.emplace (
86- type_idx, [func = std::forward<EventHandler>(handler)](auto value) {
87- func (std::any_cast<EventType>(value));
88- });
89-
90- handle = static_cast <const void *>(&(it->second ));
91- });
92- }
93- return {handle, this };
87+ return register_handler_impl<EventType>(std::forward<EventHandler>(handler));
9488 }
9589
9690 /* *
@@ -99,38 +93,46 @@ namespace dp {
9993 * @tparam ClassType Event handler class
10094 * @tparam MemberFunction Event handler member function
10195 * @param class_instance Instance of ClassType that will handle the event.
102- * @param function Pointer to the MemberFunction of the ClassType.
96+ * @param function Pointer to the MemberFunction of the ClassType. This invocation is for
97+ * when `function` takes the EventType as an argument.
10398 * @return A handler_registration instance for the given handler.
10499 */
105- template <typename EventType, typename ClassType, typename MemberFunction>
100+ template <typename ClassType, typename MemberFunction>
106101 [[nodiscard]] handler_registration register_handler (ClassType* class_instance,
107102 MemberFunction&& function) noexcept {
108- using traits = detail::function_traits<MemberFunction>;
109- static_assert (std::is_same_v<ClassType, std::decay_t <typename traits::owner_type>>,
110- " Member function pointer must match instance type." );
103+ using EventType =
104+ typename detail::function_traits<MemberFunction>::template arg<0 >::type;
111105
112- const auto type_idx = std::type_index (typeid (EventType));
113- const void * handle;
106+ static_assert (
107+ std::is_invocable_v<MemberFunction, ClassType*, EventType>,
108+ " EventHandler must be a member function of ClassType and one EventType argument." );
114109
115- if constexpr (traits::arity == 0 ) {
116- safe_unique_registrations_access ([&]() {
117- auto it = handler_registrations_.emplace (
118- type_idx,
119- [class_instance, function](auto ) { (class_instance->*function)(); });
110+ return register_handler_impl<EventType>(
111+ [class_instance, func = std::forward<MemberFunction>(function)](
112+ const EventType& event) { (class_instance->*func)(event); });
113+ }
120114
121- handle = static_cast <const void *>(&(it->second ));
122- });
123- } else {
124- safe_unique_registrations_access ([&]() {
125- auto it = handler_registrations_.emplace (
126- type_idx, [class_instance, function](auto value) {
127- (class_instance->*function)(std::any_cast<EventType>(value));
128- });
115+ /* *
116+ * @brief Register an event handler for a given event type.
117+ * @tparam EventType The event type
118+ * @tparam ClassType Event handler class
119+ * @tparam MemberFunction Event handler member function
120+ * @param class_instance Instance of ClassType that will handle the event.
121+ * @param function Pointer to the MemberFunction of the ClassType. This invocation is for
122+ * when `function` takes no arguments but wants to be fired when EventType is fired.
123+ * @return A handler_registration instance for the given handler.
124+ */
125+ template <typename EventType, typename ClassType, typename MemberFunction>
126+ [[nodiscard]] handler_registration register_handler (ClassType* class_instance,
127+ MemberFunction&& function) noexcept {
128+ static_assert (
129+ std::is_invocable_v<MemberFunction, ClassType*>,
130+ " EventHandler must be a member function of ClassType and take no arguments." );
129131
130- handle = static_cast <const void *>(&(it->second ));
132+ return register_handler_impl<EventType>(
133+ [class_instance, func = std::forward<MemberFunction>(function)]() {
134+ (class_instance->*func)();
131135 });
132- }
133- return {handle, this };
134136 }
135137
136138 /* *
@@ -139,13 +141,14 @@ namespace dp {
139141 * @param evt The event to pass to all event handlers.
140142 */
141143 template <typename EventType, typename = std::enable_if_t <!std::is_pointer_v<EventType>>>
142- void fire_event (EventType& & evt) noexcept {
143- safe_shared_registrations_access ([this , local_event = std::forward<EventType>( evt) ]() {
144+ void fire_event (const EventType& evt) noexcept {
145+ safe_shared_registrations_access ([this , & evt]() {
144146 // only call the functions we need to
145147 for (auto [begin_evt_id, end_evt_id] =
146148 handler_registrations_.equal_range (std::type_index (typeid (EventType)));
147149 begin_evt_id != end_evt_id; ++begin_evt_id) {
148- begin_evt_id->second (local_event);
150+ // call all handlers by passing a std::reference_wrapper<const EventType>
151+ begin_evt_id->second (std::cref (evt));
149152 }
150153 });
151154 }
@@ -199,26 +202,70 @@ namespace dp {
199202 handler_registrations_;
200203
201204 template <typename Callable>
202- void safe_shared_registrations_access (Callable&& callable) {
205+ void safe_shared_registrations_access (Callable&& callable) noexcept {
203206 try {
204- std::shared_lock<mutex_type> lock ( registration_mutex_) ;
207+ std::scoped_lock lock{ registration_mutex_} ;
205208 callable ();
206209 } catch (std::system_error&) {
207210 }
208211 }
212+
209213 template <typename Callable>
210- void safe_unique_registrations_access (Callable&& callable) {
214+ void safe_unique_registrations_access (Callable&& callable) noexcept {
211215 try {
212216 // if this fails, an exception may be thrown.
213- std::unique_lock<mutex_type> lock ( registration_mutex_) ;
217+ std::scoped_lock lock{ registration_mutex_} ;
214218 callable ();
215219 } catch (std::system_error&) {
216220 // do nothing
217221 }
218222 }
223+
224+ // Helper function which drastically cleans up the template parameterization requirements of
225+ // the users of this library. EventType is now deduced from the handler function directly
226+ // unless it takes no arguments.
227+ template <typename EventType, typename EventHandler>
228+ [[nodiscard]] handler_registration register_handler_impl (EventHandler&& handler) noexcept {
229+ using traits = detail::function_traits<EventHandler>;
230+ using RawParameterType = std::remove_cv_t <std::remove_reference_t <EventType>>;
231+
232+ const auto type_idx = std::type_index (typeid (RawParameterType));
233+ const void * handle;
234+
235+ // check if the function takes any arguments.
236+ if constexpr (traits::arity == 0 ) {
237+ safe_unique_registrations_access ([&]() {
238+ auto it = handler_registrations_.emplace (
239+ type_idx,
240+ [func = std::forward<EventHandler>(handler)](std::any&&) { func (); });
241+
242+ handle = static_cast <const void *>(&(it->second ));
243+ });
244+ } else {
245+ safe_unique_registrations_access ([&]() {
246+ auto it = handler_registrations_.emplace (
247+ type_idx, [func = std::forward<EventHandler>(handler)](std::any&& value) {
248+ std::reference_wrapper<const RawParameterType> local_event =
249+ std::any_cast<std::reference_wrapper<const RawParameterType>>(
250+ std::move (value));
251+
252+ if constexpr (std::is_rvalue_reference_v<EventType>) {
253+ static_assert (std::is_copy_constructible_v<RawParameterType>,
254+ " Event type must be copy constructible." );
255+ func (RawParameterType (local_event.get ()));
256+ } else {
257+ func (local_event);
258+ }
259+ });
260+
261+ handle = static_cast <const void *>(&(it->second ));
262+ });
263+ }
264+ return {handle, this };
265+ }
219266 };
220267
221- inline const void * handler_registration::handle () const { return handle_; }
268+ inline const void * handler_registration::handle () const noexcept { return handle_; }
222269
223270 inline void handler_registration::unregister () noexcept {
224271 if (event_bus_ && handle_) {
@@ -227,7 +274,8 @@ namespace dp {
227274 }
228275 }
229276
230- inline handler_registration::handler_registration (const void * handle, dp::event_bus* bus)
277+ inline handler_registration::handler_registration (const void * handle,
278+ dp::event_bus* bus) noexcept
231279 : handle_(handle), event_bus_(bus) {}
232280
233281 inline handler_registration::handler_registration (handler_registration&& other) noexcept
@@ -241,5 +289,5 @@ namespace dp {
241289 return *this ;
242290 }
243291
244- inline handler_registration::~handler_registration () { unregister (); }
292+ inline handler_registration::~handler_registration () noexcept { unregister (); }
245293} // namespace dp
0 commit comments