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<std::remove_cvref_t<
decltype(
args)>> > 0) {
 
  543            auto check_and_assert_lambda =
 
  544              []<
typename TupleType>(TupleType&& nonEmptyTupleArgs) {
 
  545                using FirstType = 
decltype(std::get<0>(std::forward<TupleType>(nonEmptyTupleArgs)));
 
  547                    std::is_reference_v<FirstType> && std::is_const_v<std::remove_reference_t<FirstType>>,
 
  548                    "when performing a non-threading call, args is expected to hold const references " 
  549                    "instead of values");
 
  552            check_and_assert_lambda(args);
 
  556            (std::apply)(outgoingConnection->func, args);
 
  559                senderPtr->handleSlotException(std::current_exception());
 
  562        if (AObject::isDisconnected()) {
 
  563            i = outgoingConnections.erase(i);
 
  576template <
typename... Args>
 
  579#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:577
#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