carb/delegate/detail/DelegateBase.h

File members: carb/delegate/detail/DelegateBase.h

// Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "../../Defines.h"

#include "../../Strong.h"
#include "../../container/IntrusiveList.h"
#include "../../thread/Util.h"

#include <memory>
#include <vector>

namespace carb
{
namespace delegate
{

template <class T>
class DelegateRef;

namespace detail
{

// The default executor class, which works in terms of thread IDs.
struct DefaultExec
{
    using Id = carb::thread::ThreadId;

    Id operator()() const
    {
        return carb::this_thread::getId();
    }
};

template <class M, class E, class T>
class DelegateBase;

template <class Mutex, class Exec, class... Args>
class DelegateBase<Mutex, Exec, void(Args...)>
{
public:
    using FunctionType = void(Args...);

    CARB_STRONGTYPE(Handle, size_t);
    CARB_DOC_CONSTEXPR static Handle kInvalidHandle{ 0 };

    DelegateBase() = default;

    DelegateBase(DelegateBase&& other);

    DelegateBase& operator=(DelegateBase&& other);

    ~DelegateBase();

    template <class Callable, class... BindArgs>
    Handle Bind(Handle* hOut, Callable&& func, BindArgs&&... args);

    template <class KeyType, class Callable, class... BindArgs>
    void BindWithKey(KeyType&& key, Callable&& func, BindArgs&&... args);

    template <class KeyType>
    bool Unbind(KeyType&& key);

    template <class KeyType>
    bool HasKey(KeyType&& key) const noexcept;

    bool UnbindCurrent();

    void UnbindAll();

    size_t Count() const noexcept;

    bool HasPending() const noexcept;

    bool IsEmpty() const noexcept;

    template <class KeyType>
    std::vector<std::decay_t<KeyType>> GetKeysByType() const;

    void Call(Args... args);

    void operator()(Args... args);

    void swap(DelegateBase& other);

protected:
    constexpr DelegateBase(std::nullptr_t);
    DelegateBase(const DelegateBase& copy) : m_impl(copy.m_impl)
    {
    }

    void copy(const DelegateBase& other)
    {
        m_impl = other.m_impl;
    }

    void reset()
    {
        m_impl.reset();
    }

    bool isValid() const noexcept
    {
        return m_impl != nullptr;
    }

private:
    using MutexType = Mutex;

    using ExecType = Exec;

    using ExecutorId = typename Exec::Id;

    // Forward declaration of the base class for bindings.
    struct BaseBinding;

    // Forward declaration of the keyed bindings class.
    template <class Key>
    struct KeyedBinding;

    // The container for storing bindings.
    using Container = carb::container::IntrusiveList<BaseBinding, &BaseBinding::link>;

    // Forward declaration of a call in progress.
    struct ActiveCall;

    // The container for storing active calls.
    using ActiveCallList = carb::container::IntrusiveList<ActiveCall, &ActiveCall::link>;

    // Forward declaration of our internal implementation.
    struct Impl;

    ActiveCall* lastCurrentThreadCall();
    const ActiveCall* lastCurrentThreadCall() const;
    void UnbindInternal(std::unique_lock<Mutex>& g, typename Container::iterator iter);

    static size_t nextHandle();

    std::shared_ptr<Impl> m_impl{ std::make_shared<Impl>() };
};

} // namespace detail
} // namespace delegate
} // namespace carb

#include "DelegateImpl.inl"