carb/delegate/Delegate.h
File members: carb/delegate/Delegate.h
// Copyright (c) 2021-2023, 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 "../cpp/Tuple.h"
#include "../thread/Mutex.h"
#include "../thread/Util.h"
#include <type_traits>
#include <vector>
#include <memory>
namespace carb
{
namespace delegate
{
template <class T>
class Delegate;
template <class T>
class DelegateRef;
template <class... Args>
class Delegate<void(Args...)>
{
public:
using FunctionType = void(Args...);
CARB_STRONGTYPE(Handle, size_t);
CARB_DOC_CONSTEXPR static Handle kInvalidHandle{ 0 };
Delegate() = default;
Delegate(Delegate&& other);
Delegate& operator=(Delegate&& other);
~Delegate();
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(Delegate& other);
CARB_PREVENT_COPY(Delegate);
private:
template <class U>
friend class DelegateRef;
struct BaseBinding;
template <class Key>
struct KeyedBinding;
using Container = carb::container::IntrusiveList<BaseBinding, &BaseBinding::link>;
struct ActiveCall;
using ActiveCallList = carb::container::IntrusiveList<ActiveCall, &ActiveCall::link>;
struct Impl : public std::enable_shared_from_this<Impl>
{
mutable carb::thread::mutex m_mutex;
Container m_entries;
ActiveCallList m_activeCalls;
~Impl();
};
constexpr Delegate(std::nullptr_t);
Delegate(std::shared_ptr<Impl> pImpl);
ActiveCall* lastCurrentThreadCall();
const ActiveCall* lastCurrentThreadCall() const;
void UnbindInternal(std::unique_lock<carb::thread::mutex>& g, typename Container::iterator iter);
static size_t nextHandle();
std::shared_ptr<Impl> m_impl{ std::make_shared<Impl>() };
};
template <class... Args>
class DelegateRef<void(Args...)>
{
public:
using DelegateType = Delegate<void(Args...)>;
constexpr DelegateRef() noexcept;
explicit DelegateRef(DelegateType& delegate);
DelegateRef(const DelegateRef& other);
DelegateRef(DelegateRef&& other) = default;
~DelegateRef();
DelegateRef& operator=(const DelegateRef& other);
DelegateRef& operator=(DelegateRef&& other) = default;
explicit operator bool() const noexcept;
void reset();
void reset(DelegateType& delegate);
void swap(DelegateRef& other);
DelegateType* get() const noexcept;
DelegateType& operator*() const noexcept;
DelegateType* operator->() const noexcept;
private:
DelegateType m_delegate;
};
template <class Del>
struct RefFromDelegate
{
using type = DelegateRef<typename Del::FunctionType>;
};
template <class Del>
using RefFromDelegate_t = typename RefFromDelegate<Del>::type;
} // namespace delegate
} // namespace carb
#include "DelegateImpl.inl"
CARB_INCLUDE_PURIFY_TEST({
using namespace carb::delegate;
Delegate<void()> d, d2{ Delegate<void()>{} }, d3 = Delegate<void()>();
auto b = d.Bind(nullptr, [] {});
d.Bind(nullptr, [](bool) {}, true);
d.BindWithKey(0, [] {});
d.BindWithKey(1, [](bool) {}, false);
d.Unbind(1);
d.Unbind(b);
d.HasKey(0);
d.UnbindCurrent();
d2.UnbindAll();
d.Count();
d.HasPending();
d.IsEmpty();
d.GetKeysByType<int>();
d.Call();
d();
d.swap(d3);
DelegateRef<void()> dr(d), dr2{};
DelegateRef<void()> dr3(dr);
DelegateRef<void()> dr4(std::move(dr2));
DelegateRef<void()> dr5 = std::move(dr4);
DelegateRef<void()> dr6 = dr5;
CARB_UNUSED(bool(dr6));
dr6.reset();
dr5.reset(d);
dr5.get();
(*dr).Call();
dr->Call();
});