1#ifndef ENTT_SIGNAL_SIGH_HPP
2#define ENTT_SIGNAL_SIGH_HPP
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"
22template<
typename Type>
34template<
typename Type,
typename Allocator>
53template<
typename Ret,
typename... Args,
typename Allocator>
54class sigh<Ret(Args...), Allocator> {
55 friend class sink<sigh<Ret(Args...), Allocator>>;
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>>;
63 using allocator_type = Allocator;
65 using size_type = stl::size_t;
67 using sink_type =
sink<sigh<Ret(Args...), Allocator>>;
70 sigh() noexcept(noexcept(allocator_type{}))
71 : sigh{allocator_type{}} {}
77 explicit sigh(
const allocator_type &allocator) noexcept
84 sigh(
const sigh &other)
85 : calls{other.calls} {}
92 sigh(
const sigh &other,
const allocator_type &allocator)
93 : calls{other.calls, allocator} {}
99 sigh(sigh &&other) noexcept
100 : calls{stl::move(other.calls)} {}
107 sigh(sigh &&other,
const allocator_type &allocator)
108 : calls{stl::move(other.calls), allocator} {}
118 sigh &operator=(
const sigh &other) {
128 sigh &operator=(sigh &&other)
noexcept {
137 void swap(sigh &other)
noexcept {
139 swap(calls, other.calls);
146 [[nodiscard]]
constexpr allocator_type get_allocator() const noexcept {
147 return calls.get_allocator();
154 [[nodiscard]] size_type size() const noexcept {
162 [[nodiscard]]
bool empty() const noexcept {
163 return calls.empty();
173 void publish(Args... args)
const {
174 for(
auto pos = calls.size(); pos; --pos) {
175 calls[pos - 1u](args...);
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...);
199 if constexpr(stl::is_invocable_r_v<bool, Func>) {
206 }
else if constexpr(stl::is_invocable_r_v<bool, Func, Ret>) {
207 if(func(calls[pos - 1u](args...))) {
211 func(calls[pos - 1u](args...));
217 container_type calls;
232 : disconnect{fn}, signal{
ref} {}
243 [[nodiscard]]
explicit operator bool() const noexcept {
244 return static_cast<bool>(disconnect);
288 : conn{stl::exchange(other.conn, {})} {}
307 conn = stl::exchange(other.conn, {});
325 [[nodiscard]]
explicit operator bool() const noexcept {
326 return static_cast<bool>(conn);
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;
363 template<auto Cand
idate,
typename Type>
364 static void release(Type value_or_instance,
void *signal) {
365 sink{*
static_cast<signal_type *
>(signal)}.disconnect<Candidate>(value_or_instance);
368 template<auto Cand
idate>
369 static void release(
void *signal) {
370 sink{*
static_cast<signal_type *
>(signal)}.disconnect<Candidate>();
373 template<
typename Func>
374 void disconnect_if(Func callback) {
375 auto &
ref = signal_or_assert();
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();
385 [[nodiscard]]
auto &signal_or_assert() const noexcept {
386 ENTT_ASSERT(signal !=
nullptr,
"Invalid pointer to signal");
399 sink(sigh<Ret(Args...), Allocator> &
ref) noexcept
406 [[nodiscard]]
bool empty() const noexcept {
407 return signal_or_assert().calls.empty();
415 template<auto Cand
idate>
416 connection connect() {
417 disconnect<Candidate>();
419 delegate_type call{};
420 call.template connect<Candidate>();
421 signal_or_assert().calls.push_back(stl::move(call));
424 conn.template connect<&release<Candidate>>();
425 return {conn, signal};
444 template<auto Cand
idate,
typename Type>
445 connection connect(Type &value_or_instance) {
446 disconnect<Candidate>(value_or_instance);
448 delegate_type call{};
449 call.template connect<Candidate>(value_or_instance);
450 signal_or_assert().calls.push_back(stl::move(call));
453 conn.template connect<&release<Candidate, Type &>>(value_or_instance);
454 return {conn, signal};
468 template<auto Cand
idate,
typename Type>
469 connection connect(Type *value_or_instance) {
470 disconnect<Candidate>(value_or_instance);
472 delegate_type call{};
473 call.template connect<Candidate>(value_or_instance);
474 signal_or_assert().calls.push_back(stl::move(call));
477 conn.template connect<&release<Candidate, Type *>>(value_or_instance);
478 return {conn, signal};
485 template<auto Cand
idate>
487 delegate_type call{};
488 call.template connect<Candidate>();
489 disconnect_if([&call](
const auto &elem) {
return elem == call; });
507 template<auto Cand
idate,
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; });
524 template<auto Cand
idate,
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; });
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; });
543 signal_or_assert().calls.clear();
550 [[nodiscard]]
explicit operator bool() const noexcept {
551 return signal !=
nullptr;
568template<
typename Ret,
typename... Args,
typename Allocator>
connection()
Default constructor.
void release()
Breaks the connection.
Basic delegate implementation.
Unmanaged signal handler.
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.
@ empty
Default mode, no element available.
sink(sigh< Ret(Args...), Allocator > &) -> sink< sigh< Ret(Args...), Allocator > >
Deduction guide.
scoped_connection()=default
Default constructor.
void release()
Breaks the connection.
scoped_connection(scoped_connection &&other) noexcept
Move constructor.
~scoped_connection()
Automatically breaks the link on destruction.
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.
scoped_connection(const scoped_connection &)=delete
Default copy constructor, deleted on purpose.
scoped_connection & operator=(connection other)
Acquires a connection.
scoped_connection & operator=(scoped_connection &&other) noexcept
Move assignment operator.