16#include "AUI/Common/ADeque.h"
17#include "AUI/Common/AObject.h"
18#include "AUI/Thread/AMutex.h"
19#include "AAbstractSignal.h"
20#include "AUI/Traits/values.h"
21#include "AUI/Util/ARaiiHelper.h"
22#include "AUI/Util/AEvaluationLoopException.h"
24namespace aui::detail::signal {
26template <
typename... Args>
27std::tuple<Args...> makeTupleOfCopies(std::tuple<const Args&...>
args) {
31static_assert(
requires(
const int& v) {
32 { makeTupleOfCopies(std::tie(v)) } -> aui::same_as<std::tuple<int>>;
35template <
size_t I,
typename TupleInitial,
typename TupleAll>
36auto resizeTuple(TupleInitial initial, TupleAll all) {
37 if constexpr (I == 0) {
40 return resizeTuple<I - 1>(
41 std::tuple_cat(initial, std::tie(std::get<std::tuple_size_v<TupleInitial>>(all))), all);
45template <aui::not_overloaded_lambda Lambda,
typename... Args>
46inline void callIgnoringExcessArgs(Lambda&& lambda,
const Args&...
args) {
47 static constexpr size_t EXPECTED_ARG_COUNT = std::tuple_size_v<typename lambda_info<std::decay_t<Lambda>>
::args>;
48 auto smallerTuple = resizeTuple<EXPECTED_ARG_COUNT>(std::make_tuple(), std::tie(
args...));
49 std::apply(lambda, smallerTuple);
52template <
typename Projection>
53struct projection_info {
55 aui::reflect::pointer_to_member<Projection> || aui::not_overloaded_lambda<Projection> ||
56 aui::function_pointer<Projection>,
57 "====================> ASignal: projection is required to be an pointer-to-member or not overloaded lambda or function pointer");
60template <aui::reflect::po
inter_to_member Projection>
61struct projection_info<Projection> {
63 template <
typename... T>
66 template <
typename First,
typename... T>
67 struct cat<First, std::tuple<T...>> {
68 using type = std::tuple<First, T...>;
72 using info =
typename aui::reflect::member<Projection>;
73 using return_t =
typename info::return_t;
74 using args =
typename cat<typename info::clazz&, typename info::args>::type;
77template <aui::not_overloaded_lambda Projection>
78struct projection_info<Projection> {
79 using info =
typename aui::lambda_info<Projection>;
80 using return_t =
typename info::return_t;
81 using args =
typename info::args;
84template <aui::function_po
inter Projection>
85struct projection_info<Projection> {
86 using info =
typename aui::function_info<Projection>;
87 using return_t =
typename info::return_t;
88 using args =
typename info::args;
94struct ProjectedSignal {
95 friend class ::AObject;
98 std::decay_t<Projection> projection;
100 ProjectedSignal(AnySignal& base, Projection projection) : base(base), projection(std::move(projection)) {}
102 using projection_info_t = aui::detail::signal::projection_info<Projection>;
104 using projection_returns_t =
typename projection_info_t::return_t;
106 static constexpr bool IS_PROJECTION_RETURNS_TUPLE = aui::is_tuple<projection_returns_t>;
109 std::conditional_t<IS_PROJECTION_RETURNS_TUPLE, projection_returns_t, std::tuple<projection_returns_t>>;
111 template <convertible_to<AObjectBase*> Object, not_overloaded_lambda Lambda>
112 void connect(Object objectBase, Lambda&& lambda) {
115 tuple_visitor<typename projection_info_t::args>::for_each_all([&]<
typename... ProjectionArgs>() {
116 return [invocable = std::forward<Lambda>(lambda),
117 projection = projection](
const std::decay_t<ProjectionArgs>&...
args) {
118 auto result = std::invoke(projection,
args...);
119 if constexpr (IS_PROJECTION_RETURNS_TUPLE) {
120 std::apply(invocable, std::move(result));
122 std::invoke(invocable, std::move(result));
128 operator bool()
const {
return bool(base); }
130 bool isAtSignalEmissionState() const noexcept {
131 return base.isAtSignalEmissionState();
135 template <not_overloaded_lambda Lambda>
136 auto makeRawInvocable(Lambda&& lambda)
const {
137 return tuple_visitor<emits_args_t>::for_each_all([&]<
typename... ProjectionResult>() {
138 return [lambda = std::forward<Lambda>(lambda)](
const std::decay_t<ProjectionResult>&...
args) {
139 aui::detail::signal::callIgnoringExcessArgs(lambda,
args...);
147template <
typename... Args>
150 (std::is_object_v<Args> && ...),
151 "There's no effect of specifying of non value arguments for the signal. Consider removing const and "
152 "reference modifiers.");
155 friend class AObject;
158 friend class PropertyPrecomputedTest_APropertyPrecomputed_Complex_Test;
159 friend class SignalSlotTest;
160 friend class PropertyTest;
161 friend class PropertyPrecomputedTest;
163 template <
typename AnySignal,
typename Projection>
164 friend struct aui::detail::signal::ProjectedSignal;
166 template <
typename T>
167 friend class AWatchable;
170 using func_t = std::function<void(
const Args&...)>;
171 using emits_args_t = std::tuple<Args...>;
173 template <
typename Projection>
174 auto projected(Projection&& projection)
const {
175 return aui::detail::signal::ProjectedSignal(
const_cast<ASignal&
>(*
this), std::forward<Projection>(projection));
180 std::tuple<
const Args&...> args;
182 void invokeSignal(AObject* sender) { signal.invokeSignal(sender, args); }
185 call_wrapper operator()(
const Args&... args) {
return { *
this, std::make_tuple(std::cref(args)...) }; }
188 ASignal(ASignal&&) noexcept = default;
189 ASignal(const ASignal&) noexcept {
193 ASignal& operator=(ASignal&&) noexcept = default;
194 ASignal& operator=(const ASignal&) noexcept {
199 virtual ~ASignal() noexcept = default;
208 operator
bool()
const {
return hasOutgoingConnections(); }
212 clearOutgoingConnectionsIf([&](
const _<ConnectionImpl>& p) {
return p->receiverBase ==
object.ptr(); });
215 [[nodiscard]]
bool hasOutgoingConnections() const noexcept {
216 return !mOutgoingConnections.empty();
221 mOutgoingConnections.begin(), mOutgoingConnections.end(),
222 [&](
const SenderConnectionOwner& s) { return s.value->receiverBase == object.ptr(); });
226 bool isAtSignalEmissionState() const noexcept {
227 return mLoopGuard.is_locked();
232 friend class ASignal;
234 void disconnect()
override {
235 std::unique_lock lock(AObjectBase::SIGNAL_SLOT_GLOBAL_SYNC);
236 unlinkInSenderSideOnly(lock);
237 if (!lock.owns_lock()) lock.lock();
238 unlinkInReceiverSideOnly(lock);
240 receiverBase =
nullptr;
250 ASignal* sender =
nullptr;
257 AObjectBase* receiverBase =
nullptr;
269 AObject* receiver =
nullptr;
283 bool toBeRemoved =
false;
292 void unlinkInReceiverSideOnly(std::unique_lock<ASpinlockMutex>& lock) {
295 auto receiverLocal = std::exchange(receiverBase,
nullptr);
296 if (!receiverLocal) {
303 void unlinkInSenderSideOnly(std::unique_lock<ASpinlockMutex>& lock) {
305 auto localSender = std::exchange(sender,
nullptr);
313 auto it = std::find_if(
314 localSender->mOutgoingConnections.begin(), localSender->mOutgoingConnections.end(),
315 [&](
const SenderConnectionOwner& o) { return o.value.get() == this; });
316 if (it == localSender->mOutgoingConnections.end()) {
322 auto self = std::exchange(it->value,
nullptr);
323 localSender->mOutgoingConnections.erase(it);
327 void onBeforeReceiverSideDestroyed()
override {
328 std::unique_lock lock(AObjectBase::SIGNAL_SLOT_GLOBAL_SYNC);
332 receiverBase =
nullptr;
333 unlinkInSenderSideOnly(lock);
340 struct SenderConnectionOwner {
341 _<ConnectionImpl> value =
nullptr;
343 SenderConnectionOwner() =
default;
344 explicit SenderConnectionOwner(_<ConnectionImpl> connection) noexcept : value(std::move(connection)) {}
345 SenderConnectionOwner(
const SenderConnectionOwner&) =
default;
346 SenderConnectionOwner(SenderConnectionOwner&&) noexcept = default;
347 SenderConnectionOwner& operator=(const SenderConnectionOwner& rhs) {
355 SenderConnectionOwner& operator=(SenderConnectionOwner&& rhs)
noexcept {
360 value = std::move(rhs.value);
364 ~SenderConnectionOwner() {
369 void release() noexcept {
373 std::unique_lock lock(AObjectBase::SIGNAL_SLOT_GLOBAL_SYNC);
375 value->sender =
nullptr;
376 value->unlinkInReceiverSideOnly(lock);
381 mutable AVector<SenderConnectionOwner> mOutgoingConnections;
382 ASpinlockMutex mLoopGuard;
384 void invokeSignal(AObject* sender, std::tuple<const Args&...> args = {});
386 template <aui::convertible_to<AObjectBase*> Object, aui::not_overloaded_lambda Lambda>
387 const _<ConnectionImpl>& connect(Object objectBase, Lambda&& lambda) {
388 AObject*
object =
nullptr;
389 if constexpr (std::is_base_of_v<AObject, std::remove_pointer_t<Object>>) {
392 const auto& connection = [&]() -> _<ConnectionImpl>& {
393 auto conn = _new<ConnectionImpl>();
395 conn->receiverBase = objectBase;
396 conn->receiver = object;
397 conn->func = makeRawInvocable(std::forward<Lambda>(lambda));
398 std::unique_lock lock(AObjectBase::SIGNAL_SLOT_GLOBAL_SYNC);
400 std::erase_if(mOutgoingConnections, [](
const SenderConnectionOwner& o) {
401 return o.value ==
nullptr;
404 return mOutgoingConnections.emplace_back(std::move(conn)).value;
412 _<Connection> addGenericObserver(AObjectBase* receiver, std::function<
void()> observer)
override {
413 return connect(receiver, [observer = std::move(observer)] { observer(); });
416 template <aui::not_overloaded_lambda Lambda>
417 auto makeRawInvocable(Lambda&& lambda)
const {
418 return [lambda = std::forward<Lambda>(lambda)](
const Args&...
args)
mutable {
419 aui::detail::signal::callIgnoringExcessArgs(lambda,
args...);
424 template <
typename Predicate>
425 void clearOutgoingConnectionsIf(Predicate&& predicate)
const noexcept {
431 AVector<SenderConnectionOwner> slotsToRemove;
433 slotsToRemove.reserve(mOutgoingConnections.size());
434 mOutgoingConnections.
removeIf([&slotsToRemove, predicate = std::move(predicate)](SenderConnectionOwner& p) {
435 if (predicate(p.value)) {
436 slotsToRemove << std::move(p);
442 slotsToRemove.clear();
445#include <AUI/Thread/AThread.h>
447template <
typename... Args>
448void ASignal<Args...>::invokeSignal(
AObject* sender, std::tuple<const Args&...> args) {
449 if (mOutgoingConnections.empty())
454 if (sender !=
nullptr) {
455 if (
auto sharedPtr = weakPtrFromObject(sender).lock()) {
456 senderPtr = std::move(
static_cast<_<AObject>>(sharedPtr));
460 std::unique_lock lock(AObjectBase::SIGNAL_SLOT_GLOBAL_SYNC);
461 std::unique_lock lock2(mLoopGuard, std::try_to_lock);
462 if (!lock2.owns_lock()) {
465 auto outgoingConnections = std::move(mOutgoingConnections);
467 if (!lock.owns_lock()) lock.lock();
471 if (mOutgoingConnections.empty()) {
472 mOutgoingConnections = std::move(outgoingConnections);
475 mOutgoingConnections.insert(
476 mOutgoingConnections.begin(), std::make_move_iterator(outgoingConnections.begin()),
477 std::make_move_iterator(outgoingConnections.end()));
480 for (
auto i = outgoingConnections.begin(); i != outgoingConnections.end();) {
482 if (!lock.owns_lock()) lock.lock();
483 if (outgoingConnection->toBeRemoved) {
485 i = outgoingConnections.erase(i);
489 if (outgoingConnection->receiver !=
nullptr) {
490 receiverWeakPtr = weakPtrFromObject(outgoingConnection->receiver);
491 if (outgoingConnection->receiver->isSlotsCallsOnlyOnMyThread() &&
501 if (receiverWeakPtr.lock() !=
nullptr) {
502 outgoingConnection->receiver->getThread()->enqueue(
503 [senderWeakPtr = senderPtr.
weak(),
504 receiverWeakPtr = std::move(receiverWeakPtr),
505 connection = outgoingConnection,
506 args = aui::detail::signal::makeTupleOfCopies(args)] {
508 std::is_same_v<std::tuple<std::decay_t<Args>...>, std::remove_const_t<decltype(args)>>,
509 "when performing a cross thread call, args is expected to hold values "
510 "instead of references");
511 auto receiverPtr = receiverWeakPtr.lock();
517 AObject::isDisconnected() =
false;
519 if (AObject::isDisconnected()) {
520 connection->disconnect();
524 (std::apply)(connection->func, args);
526 if (
auto senderPtr = senderWeakPtr.lock()) {
527 senderPtr->handleSlotException(std::current_exception());
536 AObject::isDisconnected() =
false;
538 if (
auto sharedPtr = receiverWeakPtr.lock()) {
539 receiverPtr = std::move(sharedPtr);
542 if constexpr (std::tuple_size_v<
decltype(
args)> > 0) {
543 using FirstType =
decltype(std::get<0>(args));
545 std::is_reference_v<FirstType>,
546 "when performing a non-threading call, args is expected to hold const references"
547 "instead of values");
551 (std::apply)(outgoingConnection->func, args);
554 senderPtr->handleSlotException(std::current_exception());
557 if (AObject::isDisconnected()) {
558 i = outgoingConnections.erase(i);
571template <
typename... Args>
574#define signals public
Base class for signal.
Definition AAbstractSignal.h:366
static void addIngoingConnectionIn(aui::no_escape< AObjectBase > object, _< Connection > connection)
Adds a connection to the specified object.
static void removeIngoingConnectionIn(aui::no_escape< AObjectBase > object, Connection &connection, std::unique_lock< ASpinlockMutex > &lock)
Removes a connection from the specified object.
Indicates an evaluation loop.
Definition AEvaluationLoopException.h:28
A base object class.
Definition AObject.h:39
static constexpr AObjectBase * GENERIC_OBSERVER
Indicates that a connection should not be explicitly linked to receiver's lifetime.
Definition AObject.h:61
Definition ARaiiHelper.h:17
void clearAllOutgoingConnectionsWith(aui::no_escape< AObjectBase > object) const noexcept override
Destroys all connections with passed receiver, if any.
Definition ASignal.h:211
bool hasOutgoingConnectionsWith(aui::no_escape< AObjectBase > object) const noexcept override
Definition ASignal.h:219
void clearAllOutgoingConnections() const noexcept override
Destroys all connections of this signal, if any.
Definition ASignal.h:210
static _< AAbstractThread > current()
void removeIf(Predicate &&predicate) noexcept
Definition AVector.h:334
An std::weak_ptr with AUI extensions.
Definition SharedPtrTypes.h:179
_weak< T > weak() const
Definition SharedPtrTypes.h:270
std::add_lvalue_reference_t< T > value() const noexcept
Dereferences the stored pointer.
Definition SharedPtrTypes.h:294
API_AUI_CORE const ACommandLineArgs & args() noexcept
ASignal< Args... > emits
A signal declaration.
Definition ASignal.h:572
#define AUI_MARK_AS_USED(variable)
Marks the variable as being used.
Definition macros.h:50
Connection handle.
Definition AAbstractSignal.h:375
An std::weak_ptr with AUI extensions.
Definition SharedPtrTypes.h:52
Does not allow escaping, allowing to accept lvalue ref, rvalue ref, shared_ptr and etc without overhe...
Definition values.h:128