2#ifndef LIBSBX_SIGNAL_SIGNAL_HPP_
3#define LIBSBX_SIGNAL_SIGNAL_HPP_
8#include <libsbx/utility/lockable.hpp>
10#include <libsbx/signals/copy_on_write.hpp>
11#include <libsbx/signals/slot.hpp>
12#include <libsbx/signals/connection.hpp>
13#include <libsbx/signals/observer.hpp>
15namespace sbx::signals {
17template<utility::lockable Lockable,
typename... Args>
21 inline constexpr static auto is_thread_safe_v = !std::is_same_v<L, utility::null_mutex>;
23 template<
typename U,
typename L>
24 using cow_type = std::conditional_t<is_thread_safe_v<L>,
copy_on_write<U>, U>;
26 template<
typename U,
typename L>
27 using cow_copy_type = std::conditional_t<is_thread_safe_v<L>,
copy_on_write<U>,
const U&>;
29 using lock_type = std::unique_lock<Lockable>;
31 using slot_ptr = signals::slot_ptr<Args...>;
32 using slot_list_type = std::vector<slot_ptr>;
39 using list_type = std::vector<group_type>;
43 using lockable_type = Lockable;
46 : _is_blocked{
false} {}
55 : _is_blocked{other._is_blocked.load()} {
56 auto lock = lock_type{_mutex};
59 swap(_slots, other._slots);
65 auto lock1 = lock_type{_mutex, std::defer_lock};
66 auto lock2 = lock_type{other._mutex, std::defer_lock};
68 std::lock(lock1, lock2);
71 swap(_slots, other._slots);
72 _is_blocked.store(other._is_blocked.exchange(_is_blocked.load()));
77 template<
typename... As>
78 auto emit(As&&... args) ->
void {
83 auto reference = _slots_reference();
85 for (
const auto& group : cow_read(reference)) {
86 for (
const auto&
slot : group.slots) {
87 std::invoke(*
slot, std::forward<As>(args)...);
92 template<
typename... As>
93 auto operator()(As&& ...args) ->
void {
94 emit(std::forward<As>(args)...);
97 template<
typename... As>
99 return connect(std::forward<As>(args)...);
102 template<std::invocable<Args...> Callable>
103 auto connect(Callable&& callable, group_id
id = 0) ->
connection {
104 using slot_type =
slot<Callable, Args...>;
106 auto slot = _make_slot<slot_type>(std::forward<Callable>(callable),
id);
110 _add_slot(std::move(
slot));
115 template<
typename MemberFnPtr,
typename Object>
116 requires (std::is_invocable_v<MemberFnPtr, Object, Args...> && !is_observer_v<Object> && !is_weak_ptr_compatible_v<Object>)
117 auto connect(MemberFnPtr&& member_fn_ptr, Object&&
object, group_id
id = 0) ->
connection {
120 auto slot = _make_slot<slot_type>(std::forward<MemberFnPtr>(member_fn_ptr), std::forward<Object>(
object),
id);
124 _add_slot(std::move(
slot));
129 template<std::invocable<Args...> Callable,
typename Trackable>
130 requires (is_weak_ptr_compatible_v<Trackable>)
131 auto connect(Callable&& callable, Trackable&& trackable, group_id
id = 0) ->
connection {
132 using signals::to_weak;
134 auto weak = to_weak(std::forward<Trackable>(trackable));
136 using slot_type =
tracked_slot<Callable,
decltype(weak), Args...>;
138 auto slot = _make_slot<slot_type>(std::forward<Callable>(callable), weak, id);
142 _add_slot(std::move(
slot));
147 template<
typename... As>
149 return connect(std::forward<As>(args)...);
152 template<
typename Callable>
154 auto disconnect(
const Callable& callable) -> std::size_t {
155 return disconnect_if ([&](
const auto&
slot) {
156 return slot->has_full_callable(callable);
160 template<
typename Object>
161 requires (!std::is_invocable_v<Object, Args...> && !std::is_member_function_pointer_v<Object>)
162 auto disconnect(
const Object&
object) -> std::size_t {
163 return disconnect_if ([&](
const auto&
slot) {
164 return slot->has_object(
object);
168 template<
typename Callable,
typename Object>
169 auto disconnect(
const Callable& callable,
const Object&
object) -> std::size_t {
170 return disconnect_if ([&] (
const auto&
slot) {
171 return slot->has_object(
object) &&
slot->has_callable(callable);
175 auto disconnect_all() ->
void {
176 auto lock = lock_type{_mutex};
181 void block()
noexcept {
182 _is_blocked.store(
true);
185 void unblock()
noexcept {
186 _is_blocked.store(
false);
189 bool blocked()
const noexcept {
190 return _is_blocked.load();
196 auto lock = lock_type{_mutex};
198 const auto index = state->index();
199 const auto group_id = state->group();
201 for (
auto& group : cow_write(_slots)) {
202 if (group.id == group_id) {
203 auto& slots = group.slots;
205 if (index < slots.size() && slots[index] && slots[index].get() == state.get()) {
206 std::swap(slots[index], slots.back());
207 slots[index]->set_index(index);
216 template<
typename Condition>
217 requires (std::is_invocable_r_v<bool, Condition, const slot_ptr&>)
218 auto disconnect_if (Condition&& condition) -> std::size_t {
219 auto lock = lock_type{_mutex};
221 auto& groups = cow_write(_slots);
223 auto count = std::size_t{0};
225 for (
auto& group : groups) {
226 auto& slots = group.slots;
227 auto i = std::size_t{0};
229 while (i < slots.size()) {
230 if (std::invoke(condition, slots[i])) {
233 swap(slots[i], slots.back());
235 slots[i]->set_index(i);
250 auto _slots_reference() -> cow_copy_type<list_type, lockable_type> {
251 auto lock = lock_type{_mutex};
256 template<
typename Slot,
typename... As>
257 auto _make_slot(As&& ...args) -> std::shared_ptr<Slot> {
258 return std::make_shared<Slot>(*
this, std::forward<As>(args)...);
261 auto _add_slot(slot_ptr&&
slot) ->
void {
262 const auto group_id =
slot->group();
264 auto lock = lock_type{_mutex};
266 auto& groups = cow_write(_slots);
269 auto entry = groups.begin();
271 while (entry != groups.end() && entry->id < group_id) {
276 if (entry == groups.end() || entry->id != group_id) {
277 entry = groups.insert(entry, group_type{slot_list_type{}, group_id});
281 slot->set_index(entry->slots.size());
282 entry->slots.push_back(std::move(
slot));
285 auto _clear() ->
void {
286 cow_write(_slots).clear();
289 lockable_type _mutex;
290 cow_type<list_type, Lockable> _slots;
291 std::atomic<bool> _is_blocked;
295template<
typename... Args>
298template<
typename... Args>
301template<
typename... Args>
A non-owning pointer that can be used to observe the value of a pointer.
Definition: observer_ptr.hpp:29
Definition: connection.hpp:60
Definition: copy_on_write.hpp:11
Definition: connection.hpp:123
Definition: signal.hpp:18
Definition: cleanable.hpp:11
Definition: function_traits.hpp:75
Definition: lockable.hpp:16