code:
#pragma once

#include <atomic>
#include <memory>
#include <mutex>
#include <thread>

namespace Nano
{

class Spin_Mutex
{
    std::atomic_bool locked = { false };

    public:

    inline void lock() noexcept
    {
        do
        {
            while (locked.load(std::memory_order_relaxed))
            {
                std::this_thread::yield();
            }
        }
        while (locked.exchange(true, std::memory_order_acquire));
    }

    inline void unlock() noexcept
    {
        locked.store(false, std::memory_order_release);
    }

    Spin_Mutex() = default;
    Spin_Mutex(Spin_Mutex const&) = delete;
    Spin_Mutex& operator= (Spin_Mutex const&) = delete;
};

//------------------------------------------------------------------------------

// Single Thread Policy
// Use this policy when you DO want performance but NO thread-safety!

class ST_Policy
{
    public:

    template <typename T, typename L>
    inline T const& copy_or_ref(T const& param, L&&) const
    {
        // Return a ref of param
        return param;
    }

    constexpr auto lock_guard() const
    {
        return false;
    }

    protected:

    using Weak_Ptr = ST_Policy*;

    constexpr auto weak_ptr()
    {
        return this;
    }

    constexpr auto observed(Weak_Ptr) const
    {
        return true;
    }

    constexpr auto visiting(Weak_Ptr observer) const
    {
        return (observer == this ? nullptr : observer);
    }

    constexpr auto unmask(Weak_Ptr observer) const
    {
        return observer;
    }

    constexpr void before_disconnect_all() const
    {

    }
};

//------------------------------------------------------------------------------

// Thread Safe Policy
// Use this policy when you DO want thread-safety but NO reentrancy!

template <typename Mutex = Spin_Mutex>
class TS_Policy
{
    mutable Mutex mutex;

    public:

    template <typename T, typename L>
    inline T const& copy_or_ref(T const& param, L&&) const
    {
        // Return a ref of param
        return param;
    }

    inline auto lock_guard() const
    {
        // All policies must implement the BasicLockable requirement
        return std::lock_guard<TS_Policy>(*const_cast<TS_Policy*>(this));
    }

    inline void lock() const
    {
        mutex.lock();
    }

    inline void unlock() noexcept
    {
        mutex.unlock();
    }

    protected:

    using Weak_Ptr = TS_Policy*;

    constexpr auto weak_ptr()
    {
        return this;
    }

    constexpr auto observed(Weak_Ptr) const
    {
        return true;
    }

    constexpr auto visiting(Weak_Ptr observer) const
    {
        return (observer == this ? nullptr : observer);
    }

    constexpr auto unmask(Weak_Ptr observer) const
    {
        return observer;
    }

    constexpr void before_disconnect_all() const
    {

    }
};

//------------------------------------------------------------------------------

// Single Thread Policy "Safe"
// Use this policy when you DO want reentrancy but NO thread-safety!

class ST_Policy_Safe
{
    public:

    template <typename T, typename L>
    inline T copy_or_ref(T const& param, L&&) const
    {
        // Return a copy of param
        return param;
    }

    constexpr auto lock_guard() const
    {
        return false;
    }

    protected:

    using Weak_Ptr = ST_Policy_Safe*;

    constexpr auto weak_ptr()
    {
        return this;
    }

    constexpr auto observed(Weak_Ptr) const
    {
        return true;
    }

    constexpr auto visiting(Weak_Ptr observer) const
    {
        return (observer == this ? nullptr : observer);
    }

    constexpr auto unmask(Weak_Ptr observer) const
    {
        return observer;
    }

    constexpr void before_disconnect_all() const
    {

    }
};

//------------------------------------------------------------------------------

// Thread Safe Policy "Safe"
// Use this policy when you DO want thread-safety AND reentrancy!

template <typename Mutex = Spin_Mutex>
class TS_Policy_Safe
{
    using Shared_Ptr = std::shared_ptr<TS_Policy_Safe>;

    Shared_Ptr tracker { this, [](...){} };
    mutable Mutex mutex;

    public:

    template <typename T, typename L>
    inline T copy_or_ref(T const& param, L&& lock) const
    {
        std::unique_lock<TS_Policy_Safe> unlock_after_copy = std::move(lock);
        // Return a copy of param and then unlock the now "sunk" lock
        return param;
    }

    inline auto lock_guard() const
    {
        // Unique_lock must be used in order to "sink" the lock into copy_or_ref
        return std::unique_lock<TS_Policy_Safe>(*const_cast<TS_Policy_Safe*>(this));
    }

    inline void lock() const
    {
        mutex.lock();
    }

    inline void unlock() noexcept
    {
        mutex.unlock();
    }

    protected:

    using Weak_Ptr = std::weak_ptr<TS_Policy_Safe>;

    inline Weak_Ptr weak_ptr() const
    {
        return tracker;
    }

    inline Shared_Ptr observed(Weak_Ptr observer) const
    {
        return observer.lock();
    }

    inline Shared_Ptr visiting(Weak_Ptr observer) const
    {
        // Lock the observer if it isn't "this"
        return observer.owner_before(tracker)
            || tracker.owner_before(observer) ? observer.lock() : nullptr;
    }

    inline auto unmask(Shared_Ptr& observer) const
    {
        return observer.get();
    }

    inline void before_disconnect_all()
    {
        // Immediately create a weak ptr so we can "ping" for expiration
        Weak_Ptr ping { tracker };
        // Reset the tracker and then ping for any lingering refs
        tracker.reset();
        // Wait for all visitors to finish their emissions
        do
        {
            while (!ping.expired())
            {
                std::this_thread::yield();
            }
        }
        while (ping.lock());
    }
};

} // namespace Nano ------------------------------------------------------------


code:
#pragma once

#include <algorithm>
#include <vector>

#include "nano_function.hpp"
#include "nano_mutex.hpp"

namespace Nano
{

template <typename MT_Policy = ST_Policy>
class Observer : private MT_Policy
{
    // Only Nano::Signal is allowed private access
    template <typename, typename> friend class Signal;

    struct Connection
    {
        Delegate_Key delegate;
        typename MT_Policy::Weak_Ptr observer;

        Connection() noexcept = default;
        Connection(Delegate_Key const& key) : delegate(key), observer() {}
        Connection(Delegate_Key const& key, Observer* obs) : delegate(key), observer(obs->weak_ptr()) {}
    };

    struct Z_Order
    {
        inline bool operator()(Delegate_Key const& lhs, Delegate_Key const& rhs) const
        {
            std::size_t x = lhs[0] ^ rhs[0];
            std::size_t y = lhs[1] ^ rhs[1];
            auto k = (x < y) && x < (x ^ y);
            return lhs[k] < rhs[k];
        }

        inline bool operator()(Connection const& lhs, Connection const& rhs) const
        {
            return operator()(lhs.delegate, rhs.delegate);
        }
    };

    std::vector<Connection> connections;

    //--------------------------------------------------------------------------

    void insert(Delegate_Key const& key, Observer* obs)
    {
        auto lock = MT_Policy::lock_guard();

        auto begin = std::begin(connections);
        auto end = std::end(connections);

        connections.emplace(std::upper_bound(begin, end, key, Z_Order()), key, obs);
    }

    void remove(Delegate_Key const& key) noexcept
    {
        auto lock = MT_Policy::lock_guard();

        auto begin = std::begin(connections);
        auto end = std::end(connections);

        auto slots = std::equal_range(begin, end, key, Z_Order());
        connections.erase(slots.first, slots.second);
    }

    //--------------------------------------------------------------------------

    template <typename Function, typename... Uref>
    void for_each(Uref&&... args)
    {
        auto lock = MT_Policy::lock_guard();

        for (auto const& slot : MT_Policy::copy_or_ref(connections, lock))
        {
            if (auto observer = MT_Policy::observed(slot.observer))
            {
                Function::bind(slot.delegate)(std::forward<Uref>(args)...);
            }
        }
    }

    template <typename Function, typename Accumulate, typename... Uref>
    void for_each_accumulate(Accumulate&& accumulate, Uref&&... args)
    {
        auto lock = MT_Policy::lock_guard();

        for (auto const& slot : MT_Policy::copy_or_ref(connections, lock))
        {
            if (auto observer = MT_Policy::observed(slot.observer))
            {
                accumulate(Function::bind(slot.delegate)(std::forward<Uref>(args)...));
            }
        }
    }

    //--------------------------------------------------------------------------

    public:

    void disconnect_all() noexcept
    {
        auto lock = MT_Policy::lock_guard();

        for (auto const& slot : connections)
        {
            if (auto observer = MT_Policy::visiting(slot.observer))
            {
                static_cast<Observer*>(MT_Policy::unmask(observer))->remove(slot.delegate);
            }
        }
        connections.clear();
    }

    bool is_empty() const noexcept
    {
        auto lock = MT_Policy::lock_guard();

        return connections.empty();
    }

    protected:

    // Guideline #4: A base class destructor should be
    // either public and virtual, or protected and non-virtual.
    ~Observer()
    {
        MT_Policy::before_disconnect_all();

        disconnect_all();
    }

    Observer() = default;
    Observer(Observer const&) = delete;
    Observer& operator= (Observer const&) = delete;
};

} // namespace Nano ------------------------------------------------------------

repo_name: Antidote/openrpgeditor

license: mit


code:
#pragma once

#include "nano_function.hpp"
#include "nano_observer.hpp"

namespace Nano {
template <typename RT, typename MT_Policy = ST_Policy>
class Signal;
template <typename RT, typename MT_Policy, typename... Args>
class Signal<RT(Args...), MT_Policy> final : public Observer<MT_Policy> {
  using observer = Observer<MT_Policy>;
  using function = Function<RT(Args...)>;

  template <typename T>
  void insert_sfinae(Delegate_Key const& key, typename T::Observer* instance) {
    observer::insert(key, instance);
    instance->insert(key, this);
  }
  template <typename T>
  void remove_sfinae(Delegate_Key const& key, typename T::Observer* instance) {
    observer::remove(key);
    instance->remove(key);
  }
  template <typename T>
  void insert_sfinae(Delegate_Key const& key, ...) {
    observer::insert(key, this);
  }
  template <typename T>
  void remove_sfinae(Delegate_Key const& key, ...) {
    observer::remove(key);
  }

public:
  //-------------------------------------------------------------------CONNECT

  template <typename L>
  void connect(L* instance) {
    observer::insert(function::bind(instance), this);
  }
  template <typename L>
  void connect(L& instance) {
    connect(std::addressof(instance));
  }

  template <RT (*fun_ptr)(Args...)>
  void connect() {
    observer::insert(function::template bind<fun_ptr>(), this);
  }

  template <typename T, RT (T::*mem_ptr)(Args...)>
  void connect(T* instance) {
    insert_sfinae<T>(function::template bind<mem_ptr>(instance), instance);
  }
  template <typename T, RT (T::*mem_ptr)(Args...) const>
  void connect(T* instance) {
    insert_sfinae<T>(function::template bind<mem_ptr>(instance), instance);
  }

  template <typename T, RT (T::*mem_ptr)(Args...)>
  void connect(T& instance) {
    connect<mem_ptr, T>(std::addressof(instance));
  }
  template <typename T, RT (T::*mem_ptr)(Args...) const>
  void connect(T& instance) {
    connect<mem_ptr, T>(std::addressof(instance));
  }

  template <auto mem_ptr, typename T>
  void connect(T* instance) {
    insert_sfinae<T>(function::template bind<mem_ptr>(instance), instance);
  }
  template <auto mem_ptr, typename T>
  void connect(T& instance) {
    connect<mem_ptr, T>(std::addressof(instance));
  }

  //----------------------------------------------------------------DISCONNECT

  template <typename L>
  void disconnect(L* instance) {
    observer::remove(function::bind(instance));
  }
  template <typename L>
  void disconnect(L& instance) {
    disconnect(std::addressof(instance));
  }

  template <RT (*fun_ptr)(Args...)>
  void disconnect() {
    observer::remove(function::template bind<fun_ptr>());
  }

  template <typename T, RT (T::*mem_ptr)(Args...)>
  void disconnect(T* instance) {
    remove_sfinae<T>(function::template bind<mem_ptr>(instance), instance);
  }
  template <typename T, RT (T::*mem_ptr)(Args...) const>
  void disconnect(T* instance) {
    remove_sfinae<T>(function::template bind<mem_ptr>(instance), instance);
  }

  template <typename T, RT (T::*mem_ptr)(Args...)>
  void disconnect(T& instance) {
    disconnect<T, mem_ptr>(std::addressof(instance));
  }
  template <typename T, RT (T::*mem_ptr)(Args...) const>
  void disconnect(T& instance) {
    disconnect<T, mem_ptr>(std::addressof(instance));
  }

  template <auto mem_ptr, typename T>
  void disconnect(T* instance) {
    remove_sfinae<T>(function::template bind<mem_ptr>(instance), instance);
  }
  template <auto mem_ptr, typename T>
  void disconnect(T& instance) {
    disconnect<mem_ptr, T>(std::addressof(instance));
  }

  //----------------------------------------------------FIRE / FIRE ACCUMULATE

  template <typename... Uref>
  void fire(Uref&&... args) {
    observer::template for_each<function>(std::forward<Uref>(args)...);
  }

  template <typename Accumulate, typename... Uref>
  void fire_accumulate(Accumulate&& accumulate, Uref&&... args) {
    observer::template for_each_accumulate<function, Accumulate>(std::forward<Accumulate>(accumulate), std::forward<Uref>(args)...);
  }
};

} // namespace Nano


code:
Unit Tests
==========

Currently nano-signal-slot now uses Visual Studio for all tests.

| Test Class | Test Method | Status |
|------------|-------------|-------------|
| Test_Observer_Destruction | | |
| | Test_Stack_Foo_Observer_Destructor | PASS |
| | Test_Stack_Bar_Foo_Observer_Destructor | PASS |
| | Test_Heap_Foo_Observer_Destructor | PASS |
| | Test_Heap_Bar_Foo_Observer_Destructor | PASS |
| Test_Signal_Connect | | |
| | Test_Member_Connect | PASS |
| | Test_Const_Member_Connect | PASS |
| | Test_Overloaded_Member_Connect | PASS |
| | Test_Overloaded_Static_Member_Connect | PASS |
| | Test_Overloaded_Virtual_Member_Connect | PASS |
| | Test_Overloaded_Virtual_Derived_Member_Connect | PASS |
| | Test_Overloaded_Free_Function_Connect | PASS |
| Test_Signal_Disconnect | | |
| | Test_Member_Disconnect | PASS |
| | Test_Const_Member_Disconnect | PASS |
| | Test_Overloaded_Member_Disconnect | PASS |
| | Test_Overloaded_Static_Member_Disconnect | PASS |
| | Test_Overloaded_Virtual_Member_Disconnect | PASS |
| | Test_Overloaded_Virtual_Derived_Member_Disconnect | PASS |
| | Test_Overloaded_Free_Function_Disconnect | PASS |
| Test_Signal_Edge_Cases | | |
| | Test_Fired_Disconnect | PASS |
| | Test_Fire_Disconnects | PASS |
| | Test_Fire_Connects | PASS |
| | Test_Fire_Disconnect_All | PASS |
| | Test_Fire_Recursive_Fire | — |
| | Test_Signal_Copy | — |
| Test_Signal_Fire | | |
| | Test_Member_Fire | PASS |
| | Test_Const_Member_Fire | PASS |
| | Test_Overloaded_Member_Fire | PASS |
| | Test_Overloaded_Static_Member_Fire | PASS |
| | Test_Overloaded_Virtual_Member_Fire | PASS |
| | Test_Overloaded_Virtual_Derived_Member_Fire | PASS |
| | Test_Fire_Accumulate | PASS |
| Test_ST_Policy | | |
| | Test_Global_Signal | PASS |
| Test_ST_Policy_Safe | | |
| | Test_Global_Signal | PASS |
| Test_TS_Policy | | |
| | Test_Shared_Signal | PASS |
| Test_TS_Policy_Safe | | |
| | Test_Shared_Signal | PASS |

_**Dashes currently denote unsupported use cases.*_


code:
#pragma once

#include <iostream>
#include <functional>
#include <random>

#include "../nano_signal_slot.hpp"
#include "../nano_mutex.hpp"

namespace Nano_Tests
{

    namespace
    {
        static void anonymous_output(const char* fn, const char* sl, std::size_t ln)
        {
            std::cout << fn << " LINE: " << __LINE__ << " Test: " << sl << " LINE: " << ln << std::endl;
        }
    }

    using Rng = std::minstd_rand;

    using Observer = Nano::Observer<>;
    using Signal_One = Nano::Signal<void(const char*)>;
    using Signal_Two = Nano::Signal<void(const char*, std::size_t)>;

    using Observer_ST = Nano::Observer<Nano::ST_Policy>;
    using Signal_Rng_ST = Nano::Signal<void(Rng&), Nano::ST_Policy>;

    using Observer_STS = Nano::Observer<Nano::ST_Policy_Safe>;
    using Signal_Rng_STS = Nano::Signal<void(Rng&), Nano::ST_Policy_Safe>;

    using Observer_TS = Nano::Observer<Nano::TS_Policy<>>;
    using Signal_Rng_TS = Nano::Signal<void(Rng&), Nano::TS_Policy<>>;

    using Observer_TSS = Nano::Observer<Nano::TS_Policy_Safe<>>;
    using Signal_Rng_TSS = Nano::Signal<void(Rng&), Nano::TS_Policy_Safe<>>;

    using Delegate_One = std::function<void(const char*)>;
    using Delegate_Two = std::function<void(const char*, std::size_t)>;

    //--------------------------------------------------------------------------

    class Foo : public Observer
    {
        public:

        void slot_member_signature_one(const char* sl)
        {
            anonymous_output(__FUNCTION__, sl, __LINE__);
        }
        void slot_member_signature_two(const char* sl, std::size_t ln)
        {
            anonymous_output(__FUNCTION__, sl, ln);
        }

        void slot_const_member_signature_one(const char* sl) const
        {
            anonymous_output(__FUNCTION__, sl, __LINE__);
        }
        void slot_const_member_signature_two(const char* sl, std::size_t ln) const
        {
            anonymous_output(__FUNCTION__, sl, ln);
        }

        void slot_overloaded_member(const char* sl)
        {
            anonymous_output(__FUNCTION__, sl, __LINE__);
        }
        void slot_overloaded_member(const char* sl, std::size_t ln)
        {
            anonymous_output(__FUNCTION__, sl, ln);
        }

        static void slot_static_member_function(const char* sl)
        {
            anonymous_output(__FUNCTION__, sl, __LINE__);
        }
        static void slot_static_member_function(const char* sl, std::size_t ln)
        {
            anonymous_output(__FUNCTION__, sl, ln);
        }

        virtual void slot_virtual_member_function(const char* sl)
        {
            anonymous_output(__FUNCTION__, sl, __LINE__);
        }
        virtual void slot_virtual_member_function(const char* sl, std::size_t ln)
        {
            anonymous_output(__FUNCTION__, sl, ln);
        }
    };

    //--------------------------------------------------------------------------

    class Bar : public Foo
    {
        public:

        void slot_virtual_member_function(const char* sl) override
        {
            anonymous_output(__FUNCTION__, sl, __LINE__);
        }
        void slot_virtual_member_function(const char* sl, std::size_t ln) override
        {
            anonymous_output(__FUNCTION__, sl, ln);
        }
    };

    //--------------------------------------------------------------------------

    static void slot_static_free_function(const char* sl)
    {
        anonymous_output(__FUNCTION__, sl, __LINE__);
    }

    static void slot_static_free_function(const char* sl, std::size_t ln)
    {
        anonymous_output(__FUNCTION__, sl, ln);
    }

    //--------------------------------------------------------------------------

    template <typename T = Observer_TS>
    class Moo : public T
    {
        public:

        void slot_next_random(Rng& rng)
        {
            volatile std::size_t a = rng(); (void)a;
        }
    };
}


code:
#include <iostream>
#include <memory>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_Observer_Destruction)
    {
        Signal_One mo_signal_one;
        Signal_Two mo_signal_two;

        public:

        TEST_METHOD(Test_Stack_Foo_Observer_Destructor)
        {
            {
                Foo to_foo;

                mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(to_foo);
                mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(&to_foo);
            }

            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Stack_Bar_Foo_Observer_Destructor)
        {
            {
                Bar to_bar;

                mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(&to_bar);
                mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(to_bar);
            }

            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Heap_Foo_Observer_Destructor)
        {
            {
                auto to_foo = std::make_unique<Foo>();

                mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(*to_foo);
                mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(*to_foo);
            }

            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Heap_Bar_Foo_Observer_Destructor)
        {
            {
                auto to_bar = std::make_unique<Bar>();

                mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(*to_bar);
                mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(*to_bar);
            }

            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
        }
    };
}


code:
#include <list>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_ST_Policy)
    {
        const int N = 64;

        using Moo_T = Moo<Observer_ST>;
        using Subject = Signal_Rng_ST;

        TEST_METHOD(Test_Global_Signal)
        {
            Subject subject;

            std::size_t count = 0;

            Rng rng;

            for (; count < N; ++count)
            {
                std::list<Moo_T> moo(N);

                for (auto& moo_instance : moo)
                {
                    subject.connect<&Moo_T::slot_next_random>(moo_instance);
                }
                subject.fire(rng);
            }
            Assert::IsTrue(subject.is_empty(), L"A signal was found not empty.");
        }
    };
}


code:
#include <list>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_ST_Policy_Safe)
    {
        const int N = 64;

        using Moo_T = Moo<Observer_STS>;
        using Subject = Signal_Rng_STS;

        TEST_METHOD(Test_Global_Signal)
        {
            Subject subject;

            std::size_t count = 0;

            Rng rng;

            for (; count < N; ++count)
            {
                std::list<Moo_T> moo(N);

                for (auto& moo_instance : moo)
                {
                    subject.connect<&Moo_T::slot_next_random>(moo_instance);
                }
                subject.fire(rng);
            }
            Assert::IsTrue(subject.is_empty(), L"A signal was found not empty.");
        }
    };
}


code:
#include <iostream>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_Signal_Connect)
    {
        Signal_One mo_signal_one;
        Signal_Two mo_signal_two;

        Foo mo_foo;
        Bar mo_bar;

        public:

        TEST_METHOD(Test_Member_Connect)
        {
            mo_signal_one.connect<&Foo::slot_member_signature_one>(mo_foo);
            mo_signal_one.connect<&Foo::slot_member_signature_one>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");
            Assert::IsFalse(mo_foo.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<&Foo::slot_member_signature_two>(mo_foo);
            mo_signal_two.connect<&Foo::slot_member_signature_two>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");
        }

        TEST_METHOD(Test_Const_Member_Connect)
        {
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(mo_foo);
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");
            Assert::IsFalse(mo_foo.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<&Foo::slot_const_member_signature_two>(mo_foo);
            mo_signal_two.connect<&Foo::slot_const_member_signature_two>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");
        }

        TEST_METHOD(Test_Overloaded_Member_Connect)
        {
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");
            Assert::IsFalse(mo_foo.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_two.connect<Foo, &Foo::slot_overloaded_member>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");
        }

        TEST_METHOD(Test_Overloaded_Static_Member_Connect)
        {
            mo_signal_one.connect<&Foo::slot_static_member_function>();
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<&Foo::slot_static_member_function>();
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");
        }

        TEST_METHOD(Test_Overloaded_Virtual_Member_Connect)
        {
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");
            Assert::IsFalse(mo_foo.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");
        }

        TEST_METHOD(Test_Overloaded_Virtual_Derived_Member_Connect)
        {
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(&mo_bar);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");
            Assert::IsFalse(mo_bar.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(&mo_bar);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");
        }

        TEST_METHOD(Test_Overloaded_Free_Function_Connect)
        {
            mo_signal_one.connect<&slot_static_free_function>();
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<&slot_static_free_function>();
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");
        }
    };
}


code:
#include <iostream>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_Signal_Disconnect)
    {
        Signal_One mo_signal_one;
        Signal_Two mo_signal_two;

        Foo mo_foo;
        Bar mo_bar;

        public:

        TEST_METHOD(Test_Member_Disconnect)
        {
            mo_signal_one.connect<&Foo::slot_member_signature_one>(mo_foo);
            mo_signal_one.connect<&Foo::slot_member_signature_one>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_one.disconnect<&Foo::slot_member_signature_one>(mo_foo);
            mo_signal_one.disconnect<&Foo::slot_member_signature_one>(&mo_foo);
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");

            //------------------------------------------------------------------

            mo_signal_two.connect<&Foo::slot_member_signature_two>(mo_foo);
            mo_signal_two.connect<&Foo::slot_member_signature_two>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            mo_signal_two.disconnect<&Foo::slot_member_signature_two>(mo_foo);
            mo_signal_two.disconnect<&Foo::slot_member_signature_two>(&mo_foo);
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Const_Member_Disconnect)
        {
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(mo_foo);
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_one.disconnect<&Foo::slot_const_member_signature_one>(mo_foo);
            mo_signal_one.disconnect<&Foo::slot_const_member_signature_one>(&mo_foo);
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");

            //------------------------------------------------------------------

            mo_signal_two.connect<&Foo::slot_const_member_signature_two>(mo_foo);
            mo_signal_two.connect<&Foo::slot_const_member_signature_two>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            mo_signal_two.disconnect<&Foo::slot_const_member_signature_two>(mo_foo);
            mo_signal_two.disconnect<&Foo::slot_const_member_signature_two>(&mo_foo);
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Overloaded_Member_Disconnect)
        {
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_one.disconnect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_one.disconnect<Foo, &Foo::slot_overloaded_member>(&mo_foo);
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");

            //------------------------------------------------------------------

            mo_signal_two.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_two.connect<Foo, &Foo::slot_overloaded_member>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            mo_signal_two.disconnect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_two.disconnect<Foo, &Foo::slot_overloaded_member>(&mo_foo);
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Overloaded_Static_Member_Disconnect)
        {
            mo_signal_one.connect<&Foo::slot_static_member_function>();
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<&Foo::slot_static_member_function>();
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            //------------------------------------------------------------------

            mo_signal_one.disconnect<&Foo::slot_static_member_function>();
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");

            mo_signal_two.disconnect<&Foo::slot_static_member_function>();
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Overloaded_Virtual_Member_Disconnect)
        {
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(&mo_foo);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_one.disconnect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_one.disconnect<Foo, &Foo::slot_virtual_member_function>(&mo_foo);
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");

            //------------------------------------------------------------------

            mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(&mo_foo);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            mo_signal_two.disconnect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_two.disconnect<Foo, &Foo::slot_virtual_member_function>(&mo_foo);
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Overloaded_Virtual_Derived_Member_Disconnect)
        {
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(&mo_bar);
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_one.disconnect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_one.disconnect<Bar, &Bar::slot_virtual_member_function>(&mo_bar);
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_bar.is_empty(), L"A slot was found.");

            //------------------------------------------------------------------

            mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(&mo_bar);
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            mo_signal_two.disconnect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_two.disconnect<Bar, &Bar::slot_virtual_member_function>(&mo_bar);
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_bar.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Overloaded_Free_Function_Disconnect)
        {
            mo_signal_one.connect<&slot_static_free_function>();
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_two.connect<&slot_static_free_function>();
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            //------------------------------------------------------------------

            mo_signal_one.disconnect<&slot_static_free_function>();
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");

            mo_signal_two.disconnect<&slot_static_free_function>();
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Disconnect_All)
        {
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_one.connect<&slot_static_free_function>();
            Assert::IsFalse(mo_signal_one.is_empty(), L"A slot was found missing.");

            mo_signal_one.disconnect_all();
            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_bar.is_empty(), L"A slot was found.");

            //------------------------------------------------------------------

            mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_two.connect<&slot_static_free_function>();
            Assert::IsFalse(mo_signal_two.is_empty(), L"A slot was found missing.");

            mo_signal_two.disconnect_all();
            Assert::IsTrue(mo_signal_two.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_foo.is_empty(), L"A slot was found.");
            Assert::IsTrue(mo_bar.is_empty(), L"A slot was found.");
        }
    };
}


code:
#include <iostream>
#include <functional>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_Signal_Edge_Cases)
    {
        Signal_One mo_signal_one;
        Signal_Two mo_signal_two;

        Foo mo_foo;
        Bar mo_bar;

        public:

        TEST_METHOD(Test_Fired_Disconnect)
        {
            Delegate_One fo1;

            mo_signal_one.connect(fo1);
            mo_signal_one.connect<&Foo::slot_member_signature_one>(mo_foo);
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(mo_foo);
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_one.connect<&Foo::slot_static_member_function>();
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);

            fo1 = [&](const char* sl)
            {
                mo_signal_one.disconnect(fo1);
            };

            mo_signal_one.fire(__FILE__);
        }

        TEST_METHOD(Test_Fire_Disconnects)
        {
            Delegate_One fo1;

            mo_signal_one.connect(fo1);
            mo_signal_one.connect<&Foo::slot_member_signature_one>(mo_foo);
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(mo_foo);
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_one.connect<&Foo::slot_static_member_function>();
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);

            fo1 = [&](const char* sl)
            {
                mo_signal_one.disconnect(fo1);
                mo_signal_one.disconnect<&Foo::slot_member_signature_one>(mo_foo);
                mo_signal_one.disconnect<&Foo::slot_const_member_signature_one>(mo_foo);
                mo_signal_one.disconnect<Foo, &Foo::slot_overloaded_member>(mo_foo);
                mo_signal_one.disconnect<&Foo::slot_static_member_function>();
                mo_signal_one.disconnect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
                mo_signal_one.disconnect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            };

            mo_signal_one.fire(__FILE__);

            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Fire_Connects)
        {
            Delegate_One fo1;

            mo_signal_one.connect(fo1);

            fo1 = [&](const char* sl)
            {
                mo_signal_one.connect(fo1);
                mo_signal_one.connect<&Foo::slot_member_signature_one>(mo_foo);
                mo_signal_one.connect<&Foo::slot_const_member_signature_one>(mo_foo);
                mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
                mo_signal_one.connect<&Foo::slot_static_member_function>();
                mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
                mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            };

            mo_signal_one.fire(__FILE__);
        }

        TEST_METHOD(Test_Fire_Disconnect_All)
        {
            Delegate_One fo1;

            mo_signal_one.connect(fo1);
            mo_signal_one.connect<&Foo::slot_member_signature_one>(mo_foo);
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(mo_foo);
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_one.connect<&Foo::slot_static_member_function>();
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);

            fo1 = [&](const char* sl)
            {
                mo_signal_one.disconnect_all();
            };

            mo_signal_one.fire(__FILE__);

            Assert::IsTrue(mo_signal_one.is_empty(), L"A slot was found.");
        }

        TEST_METHOD(Test_Fire_Recursive_Fire)
        {
            Delegate_One fo1;

            auto limit = 0;

            mo_signal_one.connect(fo1);

            fo1 = [&](const char* sl)
            {
                if (++limit > 2)
                {
                    Assert::Fail(L"This is not a supported use case.");
                }
                mo_signal_one.fire(__FILE__);
            };
            mo_signal_one.fire(__FILE__);
        }

        TEST_METHOD(Test_Signal_Copy)
        {
            Assert::Fail(L"This is not a supported capability.");
        }
    };
}


code:
#include <iostream>
#include <vector>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_Signal_Fire)
    {
        Signal_One mo_signal_one;
        Signal_Two mo_signal_two;

        Foo mo_foo;
        Bar mo_bar;

        public:

        TEST_METHOD(Test_Member_Fire)
        {
            mo_signal_one.connect<&Foo::slot_member_signature_one>(mo_foo);
            mo_signal_two.connect<&Foo::slot_member_signature_two>(mo_foo);

            mo_signal_one.fire(__FUNCTION__);
            mo_signal_two.fire(__FUNCTION__, __LINE__);
        }

        TEST_METHOD(Test_Const_Member_Fire)
        {
            mo_signal_one.connect<&Foo::slot_const_member_signature_one>(mo_foo);
            mo_signal_two.connect<&Foo::slot_const_member_signature_two>(mo_foo);

            mo_signal_one.fire(__FUNCTION__);
            mo_signal_two.fire(__FUNCTION__, __LINE__);
        }

        TEST_METHOD(Test_Overloaded_Member_Fire)
        {
            mo_signal_one.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);
            mo_signal_two.connect<Foo, &Foo::slot_overloaded_member>(mo_foo);

            mo_signal_one.fire(__FUNCTION__);
            mo_signal_two.fire(__FUNCTION__, __LINE__);
        }

        TEST_METHOD(Test_Overloaded_Static_Member_Fire)
        {
            mo_signal_one.connect<&Foo::slot_static_member_function>();
            mo_signal_two.connect<&Foo::slot_static_member_function>();

            mo_signal_one.fire(__FUNCTION__);
            mo_signal_two.fire(__FUNCTION__, __LINE__);
        }

        TEST_METHOD(Test_Overloaded_Virtual_Member_Fire)
        {
            mo_signal_one.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);
            mo_signal_two.connect<Foo, &Foo::slot_virtual_member_function>(mo_foo);

            mo_signal_one.fire(__FUNCTION__);
            mo_signal_two.fire(__FUNCTION__, __LINE__);
        }

        TEST_METHOD(Test_Overloaded_Virtual_Derived_Member_Fire)
        {
            mo_signal_one.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);
            mo_signal_two.connect<Bar, &Bar::slot_virtual_member_function>(mo_bar);

            mo_signal_one.fire(__FUNCTION__);
            mo_signal_two.fire(__FUNCTION__, __LINE__);
        }

        TEST_METHOD(Test_Fire_Accumulate)
        {
            Nano::Signal<std::size_t(std::size_t)> signal_three;
            auto slot_three = [&](std::size_t val)
            {
                return val * val;
            };
            signal_three.connect(slot_three);

            std::vector<std::size_t> signal_return_values;
            auto accumulator = [&](std::size_t srv)
            {
                signal_return_values.push_back(srv);
            };

            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);
            signal_three.fire_accumulate(accumulator, __LINE__);

            Assert::IsTrue(signal_return_values.size() == 10, L"An SRV was found missing.");
        }
    };
}


code:
#include <future>
#include <list>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_TS_Policy)
    {
        const int N = 64;

        using Moo_T = Moo<Observer_TS>;
        using Subject = Signal_Rng_TS;

        TEST_METHOD(Test_Shared_Signal)
        {
            Subject subject;

            auto context = [&]()
            {
                std::size_t count = 0;

                Rng rng;

                for (; count < N; ++count)
                {
                    std::list<Moo_T> moo(N);

                    for (auto& moo_instance : moo)
                    {
                        subject.connect<&Moo_T::slot_next_random>(moo_instance);
                    }
                    subject.fire(rng);
                }
            };
            std::vector<std::future<void>> future_results;
            for (std::size_t i = std::thread::hardware_concurrency(); i > 0; --i)
            {
                future_results.emplace_back(std::async(std::launch::async, context));
            }
            for (auto& future_result : future_results)
            {
                future_result.get();
            }
            Assert::IsTrue(subject.is_empty(), L"A signal was found not empty.");
        }
    };
}


code:
#include <future>
#include <list>

#include "CppUnitTest.h"

#include "Test_Base.hpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Nano_Tests
{
    TEST_CLASS(Test_TS_Policy_Safe)
    {
        const int N = 64; // 4 seconds
        //const int N = 256; // 4 minutes

        using Moo_T = Moo<Observer_TSS>;
        using Subject = Signal_Rng_TSS;

        TEST_METHOD(Test_Shared_Signal)
        {
            Subject subject;

            auto context = [&]()
            {
                std::size_t count = 0;

                Rng rng;

                for (; count < N; ++count)
                {
                    std::list<Moo_T> moo(N);

                    for (auto& moo_instance : moo)
                    {
                        subject.connect<&Moo_T::slot_next_random>(moo_instance);
                    }
                    subject.fire(rng);
                }
            };
            std::vector<std::future<void>> future_results;
            for (std::size_t i = std::thread::hardware_concurrency(); i > 0; --i)
            {
                future_results.emplace_back(std::async(std::launch::async, context));
            }
            for (auto& future_result : future_results)
            {
                future_result.get();
            }
            Assert::IsTrue(subject.is_empty(), L"A signal was found not empty.");
        }
    };
}


code:
add_library(i18n_dummy
        orei18n.hpp
)
target_link_libraries(i18n_dummy PUBLIC
        moloader::moloader
)
target_include_directories(i18n_dummy PUBLIC
        ${moloader_SOURCE_DIR}/include
)