Skip to content

Commit 7dd2cd0

Browse files
author
Paul T
authored
fix: proper move semantics for event handlers (#39, thejtshow/feature/move_semantics)
Fix #38
2 parents bfb352b + 7b96d00 commit 7dd2cd0

File tree

3 files changed

+196
-100
lines changed

3 files changed

+196
-100
lines changed

demo/main.cpp

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class first_event_callback_object {
2525
public:
2626
first_event_callback_object() = default;
2727
void on_event_fired(const first_event&) { event_count_++; }
28-
void on_third_event(){};
28+
void on_third_event() {};
2929
[[nodiscard]] int get_event_count() const { return event_count_; }
3030

3131
private:
@@ -34,15 +34,15 @@ class first_event_callback_object {
3434

3535
class third_event_callback_object {
3636
public:
37-
void on_third_event(){};
37+
void on_third_event() {};
3838
};
3939

4040
class internal_registration_class {
4141
dp::handler_registration reg;
4242

4343
public:
4444
internal_registration_class(dp::event_bus& bus)
45-
: reg(std::move(bus.register_handler<first_event>([](const first_event& evt) {
45+
: reg(std::move(bus.register_handler([](const first_event& evt) {
4646
std::cout << "test class: " << evt.message << "\n";
4747
}))) {}
4848
};
@@ -52,8 +52,8 @@ int main() {
5252
event_bus evt_bus;
5353

5454
// register free function
55-
const auto reg = evt_bus.register_handler<first_event>(&free_function_event_handler);
56-
const auto third_event_reg = evt_bus.register_handler<third_event>([](const third_event& evt) {
55+
const auto reg = evt_bus.register_handler(&free_function_event_handler);
56+
const auto third_event_reg = evt_bus.register_handler([](const third_event& evt) {
5757
std::cout << "my third event handler: " << evt.value << new_line;
5858
});
5959

@@ -68,23 +68,20 @@ int main() {
6868
evt_bus.remove_handler(third_event_reg);
6969
evt_bus.fire_event(third_event{13.0});
7070

71-
const auto other_event_reg =
72-
evt_bus.register_handler<second_event>([](const second_event& other_evt) {
73-
std::cout << "first other event handler says: " << other_evt.message << std::endl;
74-
});
75-
const auto other_event_second_reg =
76-
evt_bus.register_handler<second_event>([](const second_event& other_evt) {
77-
std::cout << "second other event handler says: " << other_evt.id << " "
78-
<< other_evt.message << std::endl;
79-
});
80-
const auto dmy_evt_first_reg =
81-
evt_bus.register_handler<first_event>([](const first_event& dmy_evt) {
82-
std::cout << "third event handler says: " << dmy_evt.message << std::endl;
83-
});
71+
const auto other_event_reg = evt_bus.register_handler([](const second_event& other_evt) {
72+
std::cout << "first other event handler says: " << other_evt.message << std::endl;
73+
});
74+
const auto other_event_second_reg = evt_bus.register_handler([](const second_event& other_evt) {
75+
std::cout << "second other event handler says: " << other_evt.id << " " << other_evt.message
76+
<< std::endl;
77+
});
78+
const auto dmy_evt_first_reg = evt_bus.register_handler([](const first_event& dmy_evt) {
79+
std::cout << "third event handler says: " << dmy_evt.message << std::endl;
80+
});
8481

8582
first_event_callback_object callback_obj;
86-
const auto dmy_evt_pmr_reg = evt_bus.register_handler<first_event>(
87-
&callback_obj, &first_event_callback_object::on_event_fired);
83+
const auto dmy_evt_pmr_reg =
84+
evt_bus.register_handler(&callback_obj, &first_event_callback_object::on_event_fired);
8885
const auto thrid_event_reg_pmr = evt_bus.register_handler<third_event>(
8986
&callback_obj, &first_event_callback_object::on_third_event);
9087

eventbus/include/eventbus/event_bus.hpp

Lines changed: 113 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)