68 template<
typename Callback>
69 EventHandle*
on(
const std::string& name, Callback&& callback,
void* scope =
nullptr)
72 return on(name, adaptCallback(std::forward<Callback>(callback)), scope);
75 template<
typename Callback>
76 EventHandle*
once(
const std::string& name, Callback&& callback,
void* scope =
nullptr)
79 return once(name, adaptCallback(std::forward<Callback>(callback)), scope);
84 void* scope =
nullptr);
87 template<
typename Callback>
88 EventHandler*
off(
const std::string& name, Callback&& callback,
void* scope =
nullptr)
91 return off(name, adaptCallback(std::forward<Callback>(callback)), scope);
96 template<
typename... Args>
99 [[nodiscard]]
bool hasEvent(
const std::string& name)
const;
105 void compactRemovedHandles();
108 struct function_traits;
110 template<
typename R,
typename... FnArgs>
111 struct function_traits<R(*)(FnArgs...)>
113 using args_tuple = std::tuple<FnArgs...>;
114 static constexpr size_t arity =
sizeof...(FnArgs);
117 template<
typename R,
typename... FnArgs>
118 struct function_traits<std::function<R(FnArgs...)>>
120 using args_tuple = std::tuple<FnArgs...>;
121 static constexpr size_t arity =
sizeof...(FnArgs);
124 template<
typename C,
typename R,
typename... FnArgs>
125 struct function_traits<R(C::*)(FnArgs...) const>
127 using args_tuple = std::tuple<FnArgs...>;
128 static constexpr size_t arity =
sizeof...(FnArgs);
131 template<
typename C,
typename R,
typename... FnArgs>
132 struct function_traits<R(C::*)(FnArgs...)>
134 using args_tuple = std::tuple<FnArgs...>;
135 static constexpr size_t arity =
sizeof...(FnArgs);
139 struct function_traits : function_traits<decltype(&F::operator())> {};
141 template<
typename Arg>
142 static bool canReadArg(
const std::any& value)
144 using Value = std::remove_cvref_t<Arg>;
145 return std::any_cast<Value>(&value) !=
nullptr;
148 template<
typename Arg>
149 static decltype(
auto) readArg(
const std::any& value)
151 using Value = std::remove_cvref_t<Arg>;
152 static_assert(!std::is_lvalue_reference_v<Arg> || std::is_const_v<std::remove_reference_t<Arg>>,
153 "Event callback args cannot be non-const lvalue references.");
155 const auto* ptr = std::any_cast<Value>(&value);
156 if constexpr (std::is_reference_v<Arg>) {
157 return static_cast<const Value&
>(*ptr);
159 return static_cast<Value
>(*ptr);
163 template<
typename Tuple,
typename Callback,
size_t... I>
164 static bool invokeTuple(Callback& callback,
const EventArgs& args, std::index_sequence<I...>)
166 if (args.size() <
sizeof...(I)) {
170 if (!(canReadArg<std::tuple_element_t<I, Tuple>>(args[I]) && ...)) {
174 callback(readArg<std::tuple_element_t<I, Tuple>>(args[I])...);
178 template<
typename Callback>
181 using Fn = std::decay_t<Callback>;
182 static_assert(std::is_invocable_v<Fn, const EventArgs&> || std::is_invocable_v<Fn> ||
183 (function_traits<Fn>::arity > 0),
184 "Event callback must be invocable as fn(), fn(const EventArgs&) or fn(arg0, ...).");
186 if constexpr (std::is_invocable_v<Callback, const EventArgs&>) {
187 return [fn = std::forward<Callback>(callback)](
const EventArgs& args)
mutable {
190 }
else if constexpr (std::is_invocable_v<Callback>) {
191 return [fn = std::forward<Callback>(callback)](
const EventArgs&)
mutable {
195 using Traits = function_traits<Fn>;
196 using Tuple =
typename Traits::args_tuple;
197 return [fn = std::forward<Callback>(callback)](
const EventArgs& args)
mutable {
198 (void)invokeTuple<Tuple>(fn, args, std::make_index_sequence<Traits::arity>{});
204 std::unordered_map<std::string, std::vector<EventHandle*>> _callbacks;
207 std::unordered_map<std::string, std::vector<EventHandle*>> _callbackActive;
210 std::vector<std::unique_ptr<EventHandle>> _eventHandles;
220 auto callbacksIt = _callbacks.find(name);
221 if (callbacksIt == _callbacks.end()) {
225 std::vector<EventHandle*>* callbacks =
nullptr;
226 std::vector<EventHandle*>& callbacksInitial = callbacksIt->second;
228 auto activeIt = _callbackActive.find(name);
229 if (activeIt == _callbackActive.end()) {
231 _callbackActive[name] = callbacksInitial;
232 }
else if (activeIt->second != callbacksInitial) {
236 static thread_local std::vector<EventHandle*> tempCallbacks;
237 tempCallbacks = callbacksInitial;
238 callbacks = &tempCallbacks;
241 std::vector<EventHandle*>& activeCallbacks = callbacks ? *callbacks : _callbackActive[name];
243 eventArgs.reserve(
sizeof...(Args));
244 (eventArgs.emplace_back(std::forward<Args>(args)), ...);
246 for (
size_t i = 0; i < activeCallbacks.size(); i++) {
248 if (!evt || !evt->_callback || evt->_removed)
257 auto existingCallbackIt = _callbacks.find(name);
258 if (existingCallbackIt == _callbacks.end())
263 std::vector<EventHandle*>& existingCallback = existingCallbackIt->second;
264 auto it = std::find(existingCallback.begin(), existingCallback.end(), evt);
265 int ind = (it != existingCallback.end()) ? std::distance(existingCallback.begin(), it) : -1;
268 if (_callbackActive[name] == existingCallback) {
269 _callbackActive[name] = std::vector<EventHandle*>(existingCallback);
272 auto callbacksIt2 = _callbacks.find(name);
273 if (callbacksIt2 == _callbacks.end())
continue;
275 std::vector<EventHandle*>& callbacks2 = callbacksIt2->second;
276 callbacks2[ind]->setRemoved(
true);
277 callbacks2[ind]->_handler =
nullptr;
279 callbacks2.erase(callbacks2.begin() + ind);
281 if (callbacks2.empty()) {
282 _callbacks.erase(name);
289 _callbackActive.erase(name);
290 compactRemovedHandles();