1#ifndef LIBSBX_SIGNAL_SIGNAL_HPP_
2#define LIBSBX_SIGNAL_SIGNAL_HPP_
7#include <libsbx/signals/lockable.hpp>
8#include <libsbx/signals/copy_on_write.hpp>
9#include <libsbx/signals/slot.hpp>
10#include <libsbx/signals/connection.hpp>
11#include <libsbx/signals/observer.hpp>
13namespace sbx::signals {
15template<lockable Lockable,
typename... Args>
19 inline constexpr static auto is_thread_safe_v = !std::is_same_v<L, null_mutex>;
21 template<
typename U,
typename L>
22 using cow_type = std::conditional_t<is_thread_safe_v<L>,
copy_on_write<U>, U>;
24 template<
typename U,
typename L>
25 using cow_copy_type = std::conditional_t<is_thread_safe_v<L>,
copy_on_write<U>,
const U&>;
27 using lock_type = std::unique_lock<Lockable>;
29 using slot_ptr = signals::slot_ptr<Args...>;
30 using slot_list_type = std::vector<slot_ptr>;
37 using list_type = std::vector<group_type>;
41 using lockable_type = Lockable;
44 : _is_blocked{
false} {}
53 : _is_blocked{other._is_blocked.load()} {
54 auto lock = lock_type{_mutex};
57 swap(_slots, other._slots);
63 auto lock1 = lock_type{_mutex, std::defer_lock};
64 auto lock2 = lock_type{other._mutex, std::defer_lock};
66 std::lock(lock1, lock2);
69 swap(_slots, other._slots);
70 _is_blocked.store(other._is_blocked.exchange(_is_blocked.load()));
75 template<
typename... As>
76 auto emit(As&&... args) ->
void {
81 auto reference = _slots_reference();
83 for (
const auto& group : cow_read(reference)) {
84 for (
const auto&
slot : group.slots) {
85 std::invoke(*
slot, std::forward<As>(args)...);
90 template<
typename... As>
91 auto operator()(As&& ...args) ->
void {
92 emit(std::forward<As>(args)...);
95 template<
typename... As>
97 return connect(std::forward<As>(args)...);
100 template<std::invocable<Args...> Callable>
101 auto connect(Callable&& callable, group_id
id = 0) ->
connection {
102 using slot_type =
slot<Callable, Args...>;
104 auto slot = _make_slot<slot_type>(std::forward<Callable>(callable),
id);
108 _add_slot(std::move(
slot));
113 template<
typename MemberFnPtr,
typename Object>
114 requires (std::is_invocable_v<MemberFnPtr, Object, Args...> && !is_observer_v<Object> && !is_weak_ptr_compatible_v<Object>)
115 auto connect(MemberFnPtr&& member_fn_ptr, Object&&
object, group_id
id = 0) ->
connection {
118 auto slot = _make_slot<slot_type>(std::forward<MemberFnPtr>(member_fn_ptr), std::forward<Object>(
object),
id);
122 _add_slot(std::move(
slot));
127 template<std::invocable<Args...> Callable,
typename Trackable>
128 requires (is_weak_ptr_compatible_v<Trackable>)
129 auto connect(Callable&& callable, Trackable&& trackable, group_id
id = 0) ->
connection {
130 using signals::to_weak;
132 auto weak = to_weak(std::forward<Trackable>(trackable));
134 using slot_type =
tracked_slot<Callable,
decltype(weak), Args...>;
136 auto slot = _make_slot<slot_type>(std::forward<Callable>(callable), weak, id);
140 _add_slot(std::move(
slot));
145 template<
typename... As>
147 return connect(std::forward<As>(args)...);
150 template<
typename Callable>
152 auto disconnect(
const Callable& callable) -> std::size_t {
153 return disconnect_if([&](
const auto&
slot) {
154 return slot->has_full_callable(callable);
158 template<
typename Object>
159 requires (!std::is_invocable_v<Object, Args...> && !std::is_member_function_pointer_v<Object>)
160 auto disconnect(
const Object&
object) -> std::size_t {
161 return disconnect_if([&](
const auto&
slot) {
162 return slot->has_object(
object);
166 template<
typename Callable,
typename Object>
167 auto disconnect(
const Callable& callable,
const Object&
object) -> std::size_t {
168 return disconnect_if([&] (
const auto&
slot) {
169 return slot->has_object(
object) &&
slot->has_callable(callable);
173 auto disconnect_all() ->
void {
174 auto lock = lock_type{_mutex};
179 void block()
noexcept {
180 _is_blocked.store(
true);
183 void unblock()
noexcept {
184 _is_blocked.store(
false);
187 bool blocked()
const noexcept {
188 return _is_blocked.load();
194 auto lock = lock_type{_mutex};
196 const auto index = state->index();
197 const auto group_id = state->group();
199 for (
auto& group : cow_write(_slots)) {
200 if (group.id == group_id) {
201 auto& slots = group.slots;
203 if (index < slots.size() && slots[index] && slots[index].get() == state.get()) {
204 std::swap(slots[index], slots.back());
205 slots[index]->set_index(index);
214 template<
typename Condition>
215 requires (std::is_invocable_r_v<bool, Condition, const slot_ptr&>)
216 auto disconnect_if(Condition&& condition) -> std::size_t {
217 auto lock = lock_type{_mutex};
219 auto& groups = cow_write(_slots);
221 auto count = std::size_t{0};
223 for (
auto& group : groups) {
224 auto& slots = group.slots;
225 auto i = std::size_t{0};
227 while (i < slots.size()) {
228 if (std::invoke(condition, slots[i])) {
231 swap(slots[i], slots.back());
233 slots[i]->set_index(i);
248 auto _slots_reference() -> cow_copy_type<list_type, lockable_type> {
249 auto lock = lock_type{_mutex};
254 template<
typename Slot,
typename... As>
255 auto _make_slot(As&& ...args) -> std::shared_ptr<Slot> {
256 return std::make_shared<Slot>(*
this, std::forward<As>(args)...);
259 auto _add_slot(slot_ptr&&
slot) ->
void {
260 const auto group_id =
slot->group();
262 auto lock = lock_type{_mutex};
264 auto& groups = cow_write(_slots);
267 auto entry = groups.begin();
269 while (entry != groups.end() && entry->id < group_id) {
274 if (entry == groups.end() || entry->id != group_id) {
275 entry = groups.insert(entry, group_type{slot_list_type{}, group_id});
279 slot->set_index(entry->slots.size());
280 entry->slots.push_back(std::move(
slot));
283 auto _clear() ->
void {
284 cow_write(_slots).clear();
287 lockable_type _mutex;
288 cow_type<list_type, Lockable> _slots;
289 std::atomic<bool> _is_blocked;
293template<
typename... Args>
296template<
typename... Args>
299template<
typename... Args>
A non-owning pointer that can be used to observe the value of a pointer.
Definition: observer_ptr.hpp:27
Definition: connection.hpp:58
Definition: copy_on_write.hpp:10
Definition: connection.hpp:121
Definition: signal.hpp:16
Definition: cleanable.hpp:10
Definition: function_traits.hpp:74
Definition: lockable.hpp:15