carb/delegate/Delegate.h
File members: carb/delegate/Delegate.h
// Copyright (c) 2021-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 "detail/DelegateBase.h"
#include "../thread/Mutex.h"
namespace carb
{
namespace delegate
{
template <class T>
class Delegate;
template <class T>
class DelegateRef;
template <class... Args>
class Delegate<void(Args...)> : public detail::DelegateBase<::carb::thread::mutex, detail::DefaultExec, void(Args...)>
{
using Base = detail::DelegateBase<::carb::thread::mutex, detail::DefaultExec, void(Args...)>;
public:
Delegate() = default;
Delegate(Delegate&& other) : Base(std::move(other))
{
}
Delegate& operator=(Delegate&& other)
{
Base::swap(other);
return *this;
}
private:
template <class U>
friend class DelegateRef;
// Null constructor, only for DelegateRef use.
constexpr Delegate(std::nullptr_t) : Base(nullptr)
{
}
// Copy constructor, only for DelegateRef use.
Delegate(const Delegate& other) : Base(other)
{
}
// Copy-assign operator, only for DelegateRef use.
Delegate& operator=(const Delegate& other)
{
Base::copy(other);
return *this;
}
};
template <class... Args>
class DelegateRef<void(Args...)>
{
public:
using DelegateType = Delegate<void(Args...)>;
constexpr DelegateRef() noexcept : m_delegate{ nullptr }
{
}
explicit DelegateRef(DelegateType& delegate) : m_delegate(delegate)
{
}
DelegateRef(const DelegateRef& other) : m_delegate(other.m_delegate)
{
}
DelegateRef(DelegateRef&& other) = default;
~DelegateRef()
{
// The Delegate destructor calls UnbindAll(), which we definitely don't want. So just reset our reference.
m_delegate.reset();
}
DelegateRef& operator=(const DelegateRef& other)
{
m_delegate = other.m_delegate;
return *this;
}
DelegateRef& operator=(DelegateRef&& other) = default;
explicit operator bool() const noexcept
{
return m_delegate.isValid();
}
void reset()
{
m_delegate.reset();
}
void reset(DelegateType& delegate)
{
m_delegate = delegate;
}
void swap(DelegateRef& other)
{
m_delegate.swap(other);
}
DelegateType* get() const noexcept
{
return m_delegate.isValid() ? const_cast<DelegateType*>(&m_delegate) : nullptr;
}
DelegateType& operator*() const noexcept
{
CARB_ASSERT(*this);
return const_cast<DelegateType&>(m_delegate);
}
DelegateType* operator->() const noexcept
{
CARB_ASSERT(*this);
return const_cast<DelegateType*>(&m_delegate);
}
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
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();
});