2#ifndef LIBSBX_SIGNAL_SIGNAL_HPP_
3#define LIBSBX_SIGNAL_SIGNAL_HPP_
8#include <libsbx/signals/lockable.hpp>
9#include <libsbx/signals/copy_on_write.hpp>
10#include <libsbx/signals/slot.hpp>
11#include <libsbx/signals/connection.hpp>
12#include <libsbx/signals/observer.hpp>
14namespace sbx::signals {
16template<lockable Lockable,
typename... Args>
20 inline constexpr static auto is_thread_safe_v = !std::is_same_v<L, null_mutex>;
22 template<
typename U,
typename L>
23 using cow_type = std::conditional_t<is_thread_safe_v<L>,
copy_on_write<U>, U>;
25 template<
typename U,
typename L>
26 using cow_copy_type = std::conditional_t<is_thread_safe_v<L>,
copy_on_write<U>,
const U&>;
28 using lock_type = std::unique_lock<Lockable>;
30 using slot_ptr = signals::slot_ptr<Args...>;
31 using slot_list_type = std::vector<slot_ptr>;
38 using list_type = std::vector<group_type>;
42 using lockable_type = Lockable;
45 : _is_blocked{
false} {}
54 : _is_blocked{other._is_blocked.load()} {
55 auto lock = lock_type{_mutex};
58 swap(_slots, other._slots);
64 auto lock1 = lock_type{_mutex, std::defer_lock};
65 auto lock2 = lock_type{other._mutex, std::defer_lock};
67 std::lock(lock1, lock2);
70 swap(_slots, other._slots);
71 _is_blocked.store(other._is_blocked.exchange(_is_blocked.load()));
76 template<
typename... As>
77 auto emit(As&&... args) ->
void {
82 auto reference = _slots_reference();
84 for (
const auto& group : cow_read(reference)) {
85 for (
const auto&
slot : group.slots) {
86 std::invoke(*
slot, std::forward<As>(args)...);
91 template<
typename... As>
92 auto operator()(As&& ...args) ->
void {
93 emit(std::forward<As>(args)...);
96 template<
typename... As>
98 return connect(std::forward<As>(args)...);
101 template<std::invocable<Args...> Callable>
102 auto connect(Callable&& callable, group_id
id = 0) ->
connection {
103 using slot_type =
slot<Callable, Args...>;
105 auto slot = _make_slot<slot_type>(std::forward<Callable>(callable),
id);
109 _add_slot(std::move(
slot));
114 template<
typename MemberFnPtr,
typename Object>
115 requires (std::is_invocable_v<MemberFnPtr, Object, Args...> && !is_observer_v<Object> && !is_weak_ptr_compatible_v<Object>)
116 auto connect(MemberFnPtr&& member_fn_ptr, Object&&
object, group_id
id = 0) ->
connection {
119 auto slot = _make_slot<slot_type>(std::forward<MemberFnPtr>(member_fn_ptr), std::forward<Object>(
object),
id);
123 _add_slot(std::move(
slot));
128 template<std::invocable<Args...> Callable,
typename Trackable>
129 requires (is_weak_ptr_compatible_v<Trackable>)
130 auto connect(Callable&& callable, Trackable&& trackable, group_id
id = 0) ->
connection {
131 using signals::to_weak;
133 auto weak = to_weak(std::forward<Trackable>(trackable));
135 using slot_type =
tracked_slot<Callable,
decltype(weak), Args...>;
137 auto slot = _make_slot<slot_type>(std::forward<Callable>(callable), weak, id);
141 _add_slot(std::move(
slot));
146 template<
typename... As>
148 return connect(std::forward<As>(args)...);
151 template<
typename Callable>
153 auto disconnect(
const Callable& callable) -> std::size_t {
154 return disconnect_if ([&](
const auto&
slot) {
155 return slot->has_full_callable(callable);
159 template<
typename Object>
160 requires (!std::is_invocable_v<Object, Args...> && !std::is_member_function_pointer_v<Object>)
161 auto disconnect(
const Object&
object) -> std::size_t {
162 return disconnect_if ([&](
const auto&
slot) {
163 return slot->has_object(
object);
167 template<
typename Callable,
typename Object>
168 auto disconnect(
const Callable& callable,
const Object&
object) -> std::size_t {
169 return disconnect_if ([&] (
const auto&
slot) {
170 return slot->has_object(
object) &&
slot->has_callable(callable);
174 auto disconnect_all() ->
void {
175 auto lock = lock_type{_mutex};
180 void block()
noexcept {
181 _is_blocked.store(
true);
184 void unblock()
noexcept {
185 _is_blocked.store(
false);
188 bool blocked()
const noexcept {
189 return _is_blocked.load();
195 auto lock = lock_type{_mutex};
197 const auto index = state->index();
198 const auto group_id = state->group();
200 for (
auto& group : cow_write(_slots)) {
201 if (group.id == group_id) {
202 auto& slots = group.slots;
204 if (index < slots.size() && slots[index] && slots[index].get() == state.get()) {
205 std::swap(slots[index], slots.back());
206 slots[index]->set_index(index);
215 template<
typename Condition>
216 requires (std::is_invocable_r_v<bool, Condition, const slot_ptr&>)
217 auto disconnect_if (Condition&& condition) -> std::size_t {
218 auto lock = lock_type{_mutex};
220 auto& groups = cow_write(_slots);
222 auto count = std::size_t{0};
224 for (
auto& group : groups) {
225 auto& slots = group.slots;
226 auto i = std::size_t{0};
228 while (i < slots.size()) {
229 if (std::invoke(condition, slots[i])) {
232 swap(slots[i], slots.back());
234 slots[i]->set_index(i);
249 auto _slots_reference() -> cow_copy_type<list_type, lockable_type> {
250 auto lock = lock_type{_mutex};
255 template<
typename Slot,
typename... As>
256 auto _make_slot(As&& ...args) -> std::shared_ptr<Slot> {
257 return std::make_shared<Slot>(*
this, std::forward<As>(args)...);
260 auto _add_slot(slot_ptr&&
slot) ->
void {
261 const auto group_id =
slot->group();
263 auto lock = lock_type{_mutex};
265 auto& groups = cow_write(_slots);
268 auto entry = groups.begin();
270 while (entry != groups.end() && entry->id < group_id) {
275 if (entry == groups.end() || entry->id != group_id) {
276 entry = groups.insert(entry, group_type{slot_list_type{}, group_id});
280 slot->set_index(entry->slots.size());
281 entry->slots.push_back(std::move(
slot));
284 auto _clear() ->
void {
285 cow_write(_slots).clear();
288 lockable_type _mutex;
289 cow_type<list_type, Lockable> _slots;
290 std::atomic<bool> _is_blocked;
294template<
typename... Args>
297template<
typename... Args>
300template<
typename... Args>
A non-owning pointer that can be used to observe the value of a pointer.
Definition: observer_ptr.hpp:28
Definition: connection.hpp:59
Definition: copy_on_write.hpp:11
Definition: connection.hpp:122
Definition: signal.hpp:17
Definition: cleanable.hpp:11
Definition: function_traits.hpp:75
Definition: lockable.hpp:16