EnTT 4.0.0
Loading...
Searching...
No Matches
sigh.hpp
1#ifndef ENTT_SIGNAL_SIGH_HPP
2#define ENTT_SIGNAL_SIGH_HPP
3
4#include "../stl/cstddef.hpp"
5#include "../stl/memory.hpp"
6#include "../stl/type_traits.hpp"
7#include "../stl/utility.hpp"
8#include "../stl/vector.hpp"
9#include "delegate.hpp"
10#include "fwd.hpp"
11
12namespace entt {
13
22template<typename Type>
23class sink;
24
34template<typename Type, typename Allocator>
35class sigh;
36
53template<typename Ret, typename... Args, typename Allocator>
54class sigh<Ret(Args...), Allocator> {
55 friend class sink<sigh<Ret(Args...), Allocator>>;
56
57 using alloc_traits = stl::allocator_traits<Allocator>;
58 using delegate_type = delegate<Ret(Args...)>;
59 using container_type = stl::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>;
60
61public:
63 using allocator_type = Allocator;
65 using size_type = stl::size_t;
67 using sink_type = sink<sigh<Ret(Args...), Allocator>>;
68
70 sigh() noexcept(noexcept(allocator_type{}))
71 : sigh{allocator_type{}} {}
72
77 explicit sigh(const allocator_type &allocator) noexcept
78 : calls{allocator} {}
79
84 sigh(const sigh &other)
85 : calls{other.calls} {}
86
92 sigh(const sigh &other, const allocator_type &allocator)
93 : calls{other.calls, allocator} {}
94
99 sigh(sigh &&other) noexcept
100 : calls{stl::move(other.calls)} {}
101
107 sigh(sigh &&other, const allocator_type &allocator)
108 : calls{stl::move(other.calls), allocator} {}
109
111 ~sigh() = default;
112
118 sigh &operator=(const sigh &other) {
119 calls = other.calls;
120 return *this;
121 }
122
128 sigh &operator=(sigh &&other) noexcept {
129 swap(other);
130 return *this;
131 }
132
137 void swap(sigh &other) noexcept {
138 using stl::swap;
139 swap(calls, other.calls);
140 }
141
146 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
147 return calls.get_allocator();
148 }
149
154 [[nodiscard]] size_type size() const noexcept {
155 return calls.size();
156 }
157
162 [[nodiscard]] bool empty() const noexcept {
163 return calls.empty();
164 }
165
173 void publish(Args... args) const {
174 for(auto pos = calls.size(); pos; --pos) {
175 calls[pos - 1u](args...);
176 }
177 }
178
193 template<typename Func>
194 void collect(Func func, Args... args) const {
195 for(auto pos = calls.size(); pos; --pos) {
196 if constexpr(stl::is_void_v<Ret> || !stl::is_invocable_v<Func, Ret>) {
197 calls[pos - 1u](args...);
198
199 if constexpr(stl::is_invocable_r_v<bool, Func>) {
200 if(func()) {
201 break;
202 }
203 } else {
204 func();
205 }
206 } else if constexpr(stl::is_invocable_r_v<bool, Func, Ret>) {
207 if(func(calls[pos - 1u](args...))) {
208 break;
209 }
210 } else {
211 func(calls[pos - 1u](args...));
212 }
213 }
214 }
215
216private:
217 container_type calls;
218};
219
227class connection {
228 template<typename>
229 friend class sink;
230
231 connection(delegate<void(void *)> fn, void *ref)
232 : disconnect{fn}, signal{ref} {}
233
234public:
237 : signal{} {}
238
243 [[nodiscard]] explicit operator bool() const noexcept {
244 return static_cast<bool>(disconnect);
245 }
246
248 void release() {
249 if(disconnect) {
250 disconnect(signal);
251 disconnect.reset();
252 }
253 }
254
255private:
256 delegate<void(void *)> disconnect;
257 void *signal;
258};
259
271 scoped_connection() = default;
272
278 : conn{other} {}
279
282
288 : conn{stl::exchange(other.conn, {})} {}
289
292 conn.release();
293 }
294
300
307 conn = stl::exchange(other.conn, {});
308 return *this;
309 }
310
317 conn = other;
318 return *this;
319 }
320
325 [[nodiscard]] explicit operator bool() const noexcept {
326 return static_cast<bool>(conn);
327 }
328
330 void release() {
331 conn.release();
332 }
333
334private:
335 connection conn;
336};
337
357template<typename Ret, typename... Args, typename Allocator>
358class sink<sigh<Ret(Args...), Allocator>> {
359 using signal_type = sigh<Ret(Args...), Allocator>;
360 using delegate_type = signal_type::delegate_type;
361 using difference_type = signal_type::container_type::difference_type;
362
363 template<auto Candidate, typename Type>
364 static void release(Type value_or_instance, void *signal) {
365 sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
366 }
367
368 template<auto Candidate>
369 static void release(void *signal) {
370 sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
371 }
372
373 template<typename Func>
374 void disconnect_if(Func callback) {
375 auto &ref = signal_or_assert();
376
377 for(auto pos = ref.calls.size(); pos; --pos) {
378 if(auto &elem = ref.calls[pos - 1u]; callback(elem)) {
379 elem = stl::move(ref.calls.back());
380 ref.calls.pop_back();
381 }
382 }
383 }
384
385 [[nodiscard]] auto &signal_or_assert() const noexcept {
386 ENTT_ASSERT(signal != nullptr, "Invalid pointer to signal");
387 return *signal;
388 }
389
390public:
392 sink() noexcept
393 : signal{} {}
394
399 sink(sigh<Ret(Args...), Allocator> &ref) noexcept
400 : signal{&ref} {}
401
406 [[nodiscard]] bool empty() const noexcept {
407 return signal_or_assert().calls.empty();
408 }
409
415 template<auto Candidate>
416 connection connect() {
417 disconnect<Candidate>();
418
419 delegate_type call{};
420 call.template connect<Candidate>();
421 signal_or_assert().calls.push_back(stl::move(call));
422
423 delegate<void(void *)> conn{};
424 conn.template connect<&release<Candidate>>();
425 return {conn, signal};
426 }
427
444 template<auto Candidate, typename Type>
445 connection connect(Type &value_or_instance) {
446 disconnect<Candidate>(value_or_instance);
447
448 delegate_type call{};
449 call.template connect<Candidate>(value_or_instance);
450 signal_or_assert().calls.push_back(stl::move(call));
451
452 delegate<void(void *)> conn{};
453 conn.template connect<&release<Candidate, Type &>>(value_or_instance);
454 return {conn, signal};
455 }
456
468 template<auto Candidate, typename Type>
469 connection connect(Type *value_or_instance) {
470 disconnect<Candidate>(value_or_instance);
471
472 delegate_type call{};
473 call.template connect<Candidate>(value_or_instance);
474 signal_or_assert().calls.push_back(stl::move(call));
475
476 delegate<void(void *)> conn{};
477 conn.template connect<&release<Candidate, Type *>>(value_or_instance);
478 return {conn, signal};
479 }
480
485 template<auto Candidate>
486 void disconnect() {
487 delegate_type call{};
488 call.template connect<Candidate>();
489 disconnect_if([&call](const auto &elem) { return elem == call; });
490 }
491
507 template<auto Candidate, typename Type>
508 void disconnect(Type &value_or_instance) {
509 delegate_type call{};
510 call.template connect<Candidate>(value_or_instance);
511 disconnect_if([&call](const auto &elem) { return elem == call; });
512 }
513
524 template<auto Candidate, typename Type>
525 void disconnect(Type *value_or_instance) {
526 delegate_type call{};
527 call.template connect<Candidate>(value_or_instance);
528 disconnect_if([&call](const auto &elem) { return elem == call; });
529 }
530
536 void disconnect(const void *value_or_instance) {
537 ENTT_ASSERT(value_or_instance != nullptr, "Invalid value or instance");
538 disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; });
539 }
540
542 void disconnect() {
543 signal_or_assert().calls.clear();
544 }
545
550 [[nodiscard]] explicit operator bool() const noexcept {
551 return signal != nullptr;
552 }
553
554private:
555 signal_type *signal;
556};
557
568template<typename Ret, typename... Args, typename Allocator>
569sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>;
570
571} // namespace entt
572
573#endif
Connection class.
Definition sigh.hpp:227
connection()
Default constructor.
Definition sigh.hpp:236
void release()
Breaks the connection.
Definition sigh.hpp:248
Basic delegate implementation.
Definition delegate.hpp:52
Unmanaged signal handler.
Definition fwd.hpp:25
Sink class.
Definition fwd.hpp:22
EnTT default namespace.
Definition dense_map.hpp:25
constexpr void swap(compressed_pair< First, Second > &lhs, compressed_pair< First, Second > &rhs) noexcept
Swaps two compressed pair objects.
delegate(connect_arg_t< Candidate >) -> delegate< stl::remove_pointer_t< internal::function_pointer_t< decltype(Candidate)> > >
Deduction guide.
@ ref
Aliasing mode, non-const reference.
Definition fwd.hpp:19
@ empty
Default mode, no element available.
Definition fwd.hpp:13
sink(sigh< Ret(Args...), Allocator > &) -> sink< sigh< Ret(Args...), Allocator > >
Deduction guide.
scoped_connection()=default
Default constructor.
void release()
Breaks the connection.
Definition sigh.hpp:330
scoped_connection(scoped_connection &&other) noexcept
Move constructor.
Definition sigh.hpp:287
~scoped_connection()
Automatically breaks the link on destruction.
Definition sigh.hpp:291
scoped_connection & operator=(const scoped_connection &)=delete
Default copy assignment operator, deleted on purpose.
scoped_connection(const connection &other)
Constructs a scoped connection from a basic connection.
Definition sigh.hpp:277
scoped_connection(const scoped_connection &)=delete
Default copy constructor, deleted on purpose.
scoped_connection & operator=(connection other)
Acquires a connection.
Definition sigh.hpp:316
scoped_connection & operator=(scoped_connection &&other) noexcept
Move assignment operator.
Definition sigh.hpp:306