1105 lines
27 KiB
C++
1105 lines
27 KiB
C++
/*
|
|
DOCUMENTATION
|
|
|
|
// CONFIG
|
|
|
|
Override default asset
|
|
#define DELEGATE_ASSERT(expression, ...)
|
|
|
|
Override default static_assert
|
|
#define DELEGATE_STATIC_ASSERT(expression, msg)
|
|
|
|
Set inline allocator size (default: 32)
|
|
#define DELEGATE_INLINE_ALLOCATION_SIZE
|
|
|
|
Reassign allocation functions:
|
|
Delegates::SetAllocationCallbacks(allocFunction, freeFunc);
|
|
|
|
|
|
// USAGE
|
|
|
|
## Classes ##
|
|
- ```Delegate<RetVal, Args>```
|
|
- ```MulticastDelegate<Args>```
|
|
|
|
## Features ##
|
|
- Support for:
|
|
- Static/Global methods
|
|
- Member functions
|
|
- Lambda's
|
|
- std::shared_ptr
|
|
- Delegate object is allocated inline if it is under 32 bytes
|
|
- Add payload to delegate during bind-time
|
|
- Move operations enable optimization
|
|
|
|
## Example Usage ##
|
|
|
|
### Delegate ###
|
|
|
|
Delegate<int, float> del;
|
|
del.BindLambda([](float a, int payload)
|
|
{
|
|
std::cout << "Lambda delegate parameter: " << a << std::endl;
|
|
std::cout << "Lambda delegate payload: " << payload << std::endl;
|
|
return 10;
|
|
}, 50);
|
|
std::cout << "Lambda delegate return value: " << del.Execute(20) << std::endl;
|
|
|
|
Output:
|
|
Lambda delegate parameter: 20
|
|
Lambda delegate payload: 50
|
|
Lambda delegate return value: 10
|
|
|
|
### MulticastDelegate ###
|
|
|
|
struct Foo
|
|
{
|
|
void Bar(float a, int payload)
|
|
{
|
|
std::cout << "Raw delegate parameter: " << a << std::endl;
|
|
std::cout << "Raw delegate payload: " << payload << std::endl;
|
|
}
|
|
};
|
|
MulticastDelegate<float> del;
|
|
del.AddLambda([](float a, int payload)
|
|
{
|
|
std::cout << "Lambda delegate parameter: " << a << std::endl;
|
|
std::cout << "Lambda delegate payload: " << payload << std::endl;
|
|
}, 90);
|
|
|
|
Foo foo;
|
|
del.AddRaw(&foo, &Foo::Bar, 10);
|
|
del.Broadcast(20);
|
|
|
|
Output:
|
|
Lambda delegate parameter: 20
|
|
Lambda delegate payload: 90
|
|
Raw delegate parameter: 20
|
|
Raw delegate payload: 10
|
|
|
|
*/
|
|
|
|
#ifndef CPP_DELEGATES
|
|
#define CPP_DELEGATES
|
|
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <tuple>
|
|
#include "extern.h"
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
//////////////////// DEFINES SECTION //////////////////////////
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
#ifndef DELEGATE_ASSERT
|
|
#include <assert.h>
|
|
#define DELEGATE_ASSERT(expression, ...) assert(expression)
|
|
#endif
|
|
|
|
#ifndef DELEGATE_STATIC_ASSERT
|
|
#if __cplusplus <= 199711L
|
|
#define DELEGATE_STATIC_ASSERT(expression, msg) static_assert(expression, msg)
|
|
#else
|
|
#define DELEGATE_STATIC_ASSERT(expression, msg)
|
|
#endif
|
|
#endif
|
|
|
|
//The allocation size of delegate data.
|
|
//Delegates larger than this will be heap allocated.
|
|
#ifndef DELEGATE_INLINE_ALLOCATION_SIZE
|
|
#define DELEGATE_INLINE_ALLOCATION_SIZE 32
|
|
#endif
|
|
|
|
#define DECLARE_DELEGATE(name, ...) \
|
|
using name = Delegate<void, __VA_ARGS__>
|
|
|
|
#define DECLARE_DELEGATE_RET(name, retValue, ...) \
|
|
using name = Delegate<retValue, __VA_ARGS__>
|
|
|
|
#define DECLARE_MULTICAST_DELEGATE(name, ...) \
|
|
using name = MulticastDelegate<__VA_ARGS__>; \
|
|
using name ## Delegate = MulticastDelegate<__VA_ARGS__>::DelegateT
|
|
|
|
#define DECLARE_EVENT(name, ownerType, ...) \
|
|
class name : public MulticastDelegate<__VA_ARGS__> \
|
|
{ \
|
|
private: \
|
|
friend class ownerType; \
|
|
using MulticastDelegate::Broadcast; \
|
|
using MulticastDelegate::RemoveAll; \
|
|
using MulticastDelegate::Remove; \
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
/////////////////// INTERNAL SECTION //////////////////////////
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
#if __cplusplus >= 201703L
|
|
#define NO_DISCARD [[nodiscard]]
|
|
#else
|
|
#define NO_DISCARD
|
|
#endif
|
|
|
|
namespace _DelegatesInteral
|
|
{
|
|
template<bool IsConst, typename Object, typename RetVal, typename ...Args>
|
|
struct MemberFunction;
|
|
|
|
template<typename Object, typename RetVal, typename ...Args>
|
|
struct MemberFunction<true, Object, RetVal, Args...>
|
|
{
|
|
using Type = RetVal(Object::*)(Args...) const;
|
|
};
|
|
|
|
template<typename Object, typename RetVal, typename ...Args>
|
|
struct MemberFunction<false, Object, RetVal, Args...>
|
|
{
|
|
using Type = RetVal(Object::*)(Args...);
|
|
};
|
|
|
|
static void* (*Alloc)(size_t size) = [](size_t size) { return malloc(size); };
|
|
static void(*Free)(void* pPtr) = [](void* pPtr) { free(pPtr); };
|
|
template<typename T>
|
|
void DelegateDeleteFunc(T* pPtr)
|
|
{
|
|
pPtr->~T();
|
|
DelegateFreeFunc(pPtr);
|
|
}
|
|
}
|
|
|
|
namespace Delegates
|
|
{
|
|
using AllocateCallback = void* (*)(size_t size);
|
|
using FreeCallback = void(*)(void* pPtr);
|
|
inline void SetAllocationCallbacks(AllocateCallback allocateCallback, FreeCallback freeCallback)
|
|
{
|
|
_DelegatesInteral::Alloc = allocateCallback;
|
|
_DelegatesInteral::Free = freeCallback;
|
|
}
|
|
}
|
|
|
|
class i_delegate_base
|
|
{
|
|
public:
|
|
i_delegate_base() = default;
|
|
virtual ~i_delegate_base() noexcept = default;
|
|
virtual const void* get_owner() const { return nullptr; }
|
|
virtual void clone(void* pDestination) = 0;
|
|
};
|
|
|
|
//Base type for delegates
|
|
template<typename RetVal, typename... Args>
|
|
class i_delegate : public i_delegate_base
|
|
{
|
|
public:
|
|
virtual RetVal execute(Args&&... args) = 0;
|
|
};
|
|
|
|
template<typename RetVal, typename... Args2>
|
|
class static_delegate;
|
|
|
|
template<typename RetVal, typename... Args, typename... Args2>
|
|
class static_delegate<RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
|
|
{
|
|
public:
|
|
using DelegateFunction = RetVal(*)(Args..., Args2...);
|
|
|
|
static_delegate(DelegateFunction function, Args2&&... payload)
|
|
: function_(function), payload_(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
static_delegate(DelegateFunction function, const std::tuple<Args2...>& payload)
|
|
: function_(function), payload_(payload)
|
|
{}
|
|
|
|
virtual RetVal execute(Args&&... args) override
|
|
{
|
|
return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
new (pDestination) static_delegate(function_, payload_);
|
|
}
|
|
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
return function_(std::forward<Args>(args)..., std::get<Is>(payload_)...);
|
|
}
|
|
|
|
DelegateFunction function_;
|
|
std::tuple<Args2...> payload_;
|
|
};
|
|
|
|
template<bool IsConst, typename T, typename RetVal, typename... Args2>
|
|
class raw_delegate;
|
|
|
|
template<bool IsConst, typename T, typename RetVal, typename... Args, typename... Args2>
|
|
class raw_delegate<IsConst, T, RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
|
|
{
|
|
public:
|
|
using DelegateFunction = typename _DelegatesInteral::MemberFunction<IsConst, T, RetVal, Args..., Args2...>::Type;
|
|
|
|
raw_delegate(T* pObject, DelegateFunction function, Args2&&... payload)
|
|
: object_(pObject), function_(function), payload_(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
raw_delegate(T* pObject, DelegateFunction function, const std::tuple<Args2...>& payload)
|
|
: object_(pObject), function_(function), payload_(payload)
|
|
{}
|
|
|
|
virtual RetVal execute(Args&&... args) override
|
|
{
|
|
return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
virtual const void* get_owner() const override
|
|
{
|
|
return object_;
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
new (pDestination) raw_delegate(object_, function_, payload_);
|
|
}
|
|
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
return (object_->*function_)(std::forward<Args>(args)..., std::get<Is>(payload_)...);
|
|
}
|
|
|
|
T* object_;
|
|
DelegateFunction function_;
|
|
std::tuple<Args2...> payload_;
|
|
};
|
|
|
|
template<typename TLambda, typename RetVal, typename... Args>
|
|
class lambda_delegate;
|
|
|
|
template<typename TLambda, typename RetVal, typename... Args, typename... Args2>
|
|
class lambda_delegate<TLambda, RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
|
|
{
|
|
public:
|
|
explicit lambda_delegate(TLambda&& lambda, Args2&&... payload)
|
|
: lambda_(std::forward<TLambda>(lambda)),
|
|
payload_(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
explicit lambda_delegate(const TLambda& lambda, const std::tuple<Args2...>& payload)
|
|
: lambda_(lambda),
|
|
payload_(payload)
|
|
{}
|
|
|
|
RetVal execute(Args&&... args) override
|
|
{
|
|
return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
new (pDestination) lambda_delegate(lambda_, payload_);
|
|
}
|
|
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
return (RetVal)((lambda_)(std::forward<Args>(args)..., std::get<Is>(payload_)...));
|
|
}
|
|
|
|
TLambda lambda_;
|
|
std::tuple<Args2...> payload_;
|
|
};
|
|
|
|
template<bool IsConst, typename T, typename RetVal, typename... Args>
|
|
class sp_delegate;
|
|
|
|
template<bool IsConst, typename RetVal, typename T, typename... Args, typename... Args2>
|
|
class sp_delegate<IsConst, T, RetVal(Args...), Args2...> : public i_delegate<RetVal, Args...>
|
|
{
|
|
public:
|
|
using DelegateFunction = typename _DelegatesInteral::MemberFunction<IsConst, T, RetVal, Args..., Args2...>::Type;
|
|
|
|
sp_delegate(std::shared_ptr<T> pObject, DelegateFunction pFunction, Args2&&... payload)
|
|
: object_(pObject),
|
|
function_(pFunction),
|
|
payload_(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
sp_delegate(std::weak_ptr<T> pObject, DelegateFunction pFunction, const std::tuple<Args2...>& payload)
|
|
: object_(pObject),
|
|
function_(pFunction),
|
|
payload_(payload)
|
|
{}
|
|
|
|
virtual RetVal execute(Args&&... args) override
|
|
{
|
|
return execute_internal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
|
|
virtual const void* get_owner() const override
|
|
{
|
|
return object_.expired() ? nullptr : object_.lock().get();
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
new (pDestination) sp_delegate(object_, function_, payload_);
|
|
}
|
|
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal execute_internal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
if (object_.expired())
|
|
{
|
|
return RetVal();
|
|
}
|
|
else
|
|
{
|
|
std::shared_ptr<T> pPinned = object_.lock();
|
|
return (pPinned.get()->*function_)(std::forward<Args>(args)..., std::get<Is>(payload_)...);
|
|
}
|
|
}
|
|
|
|
std::weak_ptr<T> object_;
|
|
DelegateFunction function_;
|
|
std::tuple<Args2...> payload_;
|
|
};
|
|
|
|
//A handle to a delegate used for a multicast delegate
|
|
//Static ID so that every handle is unique
|
|
class delegate_handle
|
|
{
|
|
public:
|
|
constexpr delegate_handle() noexcept
|
|
: id_(INVALID_ID)
|
|
{
|
|
}
|
|
|
|
explicit delegate_handle(bool /*generateId*/) noexcept
|
|
: id_(get_new_id())
|
|
{
|
|
}
|
|
|
|
~delegate_handle() noexcept = default;
|
|
delegate_handle(const delegate_handle& other) = default;
|
|
delegate_handle& operator=(const delegate_handle& other) = default;
|
|
|
|
delegate_handle(delegate_handle&& other) noexcept
|
|
: id_(other.id_)
|
|
{
|
|
other.reset();
|
|
}
|
|
|
|
delegate_handle& operator=(delegate_handle&& other) noexcept
|
|
{
|
|
id_ = other.id_;
|
|
other.reset();
|
|
return *this;
|
|
}
|
|
|
|
operator bool() const noexcept
|
|
{
|
|
return is_valid();
|
|
}
|
|
|
|
bool operator==(const delegate_handle& other) const noexcept
|
|
{
|
|
return id_ == other.id_;
|
|
}
|
|
|
|
bool operator<(const delegate_handle& other) const noexcept
|
|
{
|
|
return id_ < other.id_;
|
|
}
|
|
|
|
bool is_valid() const noexcept
|
|
{
|
|
return id_ != INVALID_ID;
|
|
}
|
|
|
|
void reset() noexcept
|
|
{
|
|
id_ = INVALID_ID;
|
|
}
|
|
|
|
constexpr static const unsigned int INVALID_ID = (unsigned int)~0;
|
|
private:
|
|
unsigned int id_;
|
|
CORE_API static unsigned int CURRENT_ID;
|
|
|
|
static int get_new_id()
|
|
{
|
|
unsigned int output = delegate_handle::CURRENT_ID++;
|
|
if (delegate_handle::CURRENT_ID == INVALID_ID)
|
|
{
|
|
delegate_handle::CURRENT_ID = 0;
|
|
}
|
|
return output;
|
|
}
|
|
};
|
|
|
|
template<size_t MaxStackSize>
|
|
class inline_allocator
|
|
{
|
|
public:
|
|
//Constructor
|
|
constexpr inline_allocator() noexcept
|
|
: size_(0)
|
|
{
|
|
DELEGATE_STATIC_ASSERT(MaxStackSize > sizeof(void*), "MaxStackSize is smaller or equal to the size of a pointer. This will make the use of an InlineAllocator pointless. Please increase the MaxStackSize.");
|
|
}
|
|
|
|
//Destructor
|
|
~inline_allocator() noexcept
|
|
{
|
|
free();
|
|
}
|
|
|
|
//Copy constructor
|
|
inline_allocator(const inline_allocator& other)
|
|
: size_(0)
|
|
{
|
|
if (other.has_allocation())
|
|
{
|
|
memcpy(allocate(other.size_), other.get_allocation(), other.size_);
|
|
}
|
|
size_ = other.size_;
|
|
}
|
|
|
|
//Copy assignment operator
|
|
inline_allocator& operator=(const inline_allocator& other)
|
|
{
|
|
if (other.has_allocation())
|
|
{
|
|
memcpy(allocate(other.size_), other.get_allocation(), other.size_);
|
|
}
|
|
size_ = other.size_;
|
|
return *this;
|
|
}
|
|
|
|
//Move constructor
|
|
inline_allocator(inline_allocator&& other) noexcept
|
|
: size_(other.size_)
|
|
{
|
|
other.size_ = 0;
|
|
if (size_ > MaxStackSize)
|
|
{
|
|
std::swap(pPtr, other.pPtr);
|
|
}
|
|
else
|
|
{
|
|
memcpy(Buffer, other.Buffer, size_);
|
|
}
|
|
}
|
|
|
|
//Move assignment operator
|
|
inline_allocator& operator=(inline_allocator&& other) noexcept
|
|
{
|
|
free();
|
|
size_ = other.size_;
|
|
other.size_ = 0;
|
|
if (size_ > MaxStackSize)
|
|
{
|
|
std::swap(pPtr, other.pPtr);
|
|
}
|
|
else
|
|
{
|
|
memcpy(Buffer, other.Buffer, size_);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//Allocate memory of given size
|
|
//If the size is over the predefined threshold, it will be allocated on the heap
|
|
void* allocate(const size_t size)
|
|
{
|
|
if (size_ != size)
|
|
{
|
|
free();
|
|
size_ = size;
|
|
if (size > MaxStackSize)
|
|
{
|
|
pPtr = _DelegatesInteral::Alloc(size);
|
|
return pPtr;
|
|
}
|
|
}
|
|
return (void*)Buffer;
|
|
}
|
|
|
|
//Free the allocated memory
|
|
void free()
|
|
{
|
|
if (size_ > MaxStackSize)
|
|
{
|
|
_DelegatesInteral::Free(pPtr);
|
|
}
|
|
size_ = 0;
|
|
}
|
|
|
|
//Return the allocated memory either on the stack or on the heap
|
|
void* get_allocation() const
|
|
{
|
|
if (has_allocation())
|
|
{
|
|
return has_heap_allocation() ? pPtr : (void*)Buffer;
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
size_t get_size() const
|
|
{
|
|
return size_;
|
|
}
|
|
|
|
bool has_allocation() const
|
|
{
|
|
return size_ > 0;
|
|
}
|
|
|
|
bool has_heap_allocation() const
|
|
{
|
|
return size_ > MaxStackSize;
|
|
}
|
|
|
|
private:
|
|
//If the allocation is smaller than the threshold, Buffer is used
|
|
//Otherwise pPtr is used together with a separate dynamic allocation
|
|
union
|
|
{
|
|
char Buffer[MaxStackSize];
|
|
void* pPtr;
|
|
};
|
|
size_t size_;
|
|
};
|
|
|
|
class delegate_base
|
|
{
|
|
public:
|
|
//Default constructor
|
|
constexpr delegate_base() noexcept
|
|
: allocator_()
|
|
{}
|
|
|
|
//Default destructor
|
|
virtual ~delegate_base() noexcept
|
|
{
|
|
release();
|
|
}
|
|
|
|
//Copy contructor
|
|
delegate_base(const delegate_base& other)
|
|
{
|
|
if (other.allocator_.has_allocation())
|
|
{
|
|
allocator_.allocate(other.allocator_.get_size());
|
|
other.get_delegate()->clone(allocator_.get_allocation());
|
|
}
|
|
}
|
|
|
|
//Copy assignment operator
|
|
delegate_base& operator=(const delegate_base& other)
|
|
{
|
|
release();
|
|
if (other.allocator_.has_allocation())
|
|
{
|
|
allocator_.allocate(other.allocator_.get_size());
|
|
other.get_delegate()->clone(allocator_.get_allocation());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//Move constructor
|
|
delegate_base(delegate_base&& other) noexcept
|
|
: allocator_(std::move(other.allocator_))
|
|
{}
|
|
|
|
//Move assignment operator
|
|
delegate_base& operator=(delegate_base&& other) noexcept
|
|
{
|
|
release();
|
|
allocator_ = std::move(other.allocator_);
|
|
return *this;
|
|
}
|
|
|
|
//Gets the owner of the deletage
|
|
//Only valid for SPDelegate and RawDelegate.
|
|
//Otherwise returns nullptr by default
|
|
const void* get_owner() const
|
|
{
|
|
if (allocator_.has_allocation())
|
|
{
|
|
return get_delegate()->get_owner();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
size_t get_size() const
|
|
{
|
|
return allocator_.get_size();
|
|
}
|
|
|
|
//Clear the bound delegate if it is bound to the given object.
|
|
//Ignored when pObject is a nullptr
|
|
void clear_if_bound_to(void* pObject)
|
|
{
|
|
if (pObject != nullptr && is_bound_to(pObject))
|
|
{
|
|
release();
|
|
}
|
|
}
|
|
|
|
//Clear the bound delegate if it exists
|
|
void clear()
|
|
{
|
|
release();
|
|
}
|
|
|
|
//If the allocator has a size, it means it's bound to something
|
|
bool is_bound() const
|
|
{
|
|
return allocator_.has_allocation();
|
|
}
|
|
|
|
bool is_bound_to(void* pObject) const
|
|
{
|
|
if (pObject == nullptr || allocator_.has_allocation() == false)
|
|
{
|
|
return false;
|
|
}
|
|
return get_delegate()->get_owner() == pObject;
|
|
}
|
|
|
|
protected:
|
|
void release()
|
|
{
|
|
if (allocator_.has_allocation())
|
|
{
|
|
get_delegate()->~i_delegate_base();
|
|
allocator_.free();
|
|
}
|
|
}
|
|
|
|
i_delegate_base* get_delegate() const
|
|
{
|
|
return static_cast<i_delegate_base*>(allocator_.get_allocation());
|
|
}
|
|
|
|
//Allocator for the delegate itself.
|
|
//Delegate gets allocated when its is smaller or equal than 64 bytes in size.
|
|
//Can be changed by preference
|
|
inline_allocator<DELEGATE_INLINE_ALLOCATION_SIZE> allocator_;
|
|
};
|
|
|
|
//Delegate that can be bound to by just ONE object
|
|
template<typename RetVal, typename... Args>
|
|
class delegate : public delegate_base
|
|
{
|
|
private:
|
|
template<typename T, typename... Args2>
|
|
using ConstMemberFunction = typename _DelegatesInteral::MemberFunction<true, T, RetVal, Args..., Args2...>::Type;
|
|
template<typename T, typename... Args2>
|
|
using NonConstMemberFunction = typename _DelegatesInteral::MemberFunction<false, T, RetVal, Args..., Args2...>::Type;
|
|
|
|
public:
|
|
using IDelegateT = i_delegate<RetVal, Args...>;
|
|
|
|
//Create delegate using member function
|
|
template<typename T, typename... Args2>
|
|
NO_DISCARD static delegate create_raw(T* pObj, NonConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
delegate handler;
|
|
handler.bind<raw_delegate<false, T, RetVal(Args...), Args2...>>(pObj, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
NO_DISCARD static delegate create_raw(T* pObj, ConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
delegate handler;
|
|
handler.bind<raw_delegate<true, T, RetVal(Args...), Args2...>>(pObj, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
//Create delegate using global/static function
|
|
template<typename... Args2>
|
|
NO_DISCARD static delegate create_static(RetVal(*pFunction)(Args..., Args2...), Args2... args)
|
|
{
|
|
delegate handler;
|
|
handler.bind<static_delegate<RetVal(Args...), Args2...>>(pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
//Create delegate using std::shared_ptr
|
|
template<typename T, typename... Args2>
|
|
NO_DISCARD static delegate create_sp(const std::shared_ptr<T>& pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
delegate handler;
|
|
handler.bind<sp_delegate<false, T, RetVal(Args...), Args2...>>(pObject, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
NO_DISCARD static delegate create_sp(const std::shared_ptr<T>& pObject, ConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
delegate handler;
|
|
handler.bind<sp_delegate<true, T, RetVal(Args...), Args2...>>(pObject, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
//Create delegate using a lambda
|
|
template<typename TLambda, typename... Args2>
|
|
NO_DISCARD static delegate create_lambda(TLambda&& lambda, Args2... args)
|
|
{
|
|
delegate handler;
|
|
handler.bind<lambda_delegate<TLambda, RetVal(Args...), Args2...>>(std::forward<TLambda>(lambda), std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
//Bind a member function
|
|
template<typename T, typename... Args2>
|
|
void bind_raw(T* pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
DELEGATE_STATIC_ASSERT(!std::is_const<T>::value, "Cannot bind a non-const function on a const object");
|
|
*this = create_raw<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
void bind_raw(T* pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
*this = create_raw<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
//Bind a static/global function
|
|
template<typename... Args2>
|
|
void bind_static(RetVal(*pFunction)(Args..., Args2...), Args2&&... args)
|
|
{
|
|
*this = create_static<Args2... >(pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
//Bind a lambda
|
|
template<typename LambdaType, typename... Args2>
|
|
void bind_lambda(LambdaType&& lambda, Args2&&... args)
|
|
{
|
|
*this = create_lambda<LambdaType, Args2... >(std::forward<LambdaType>(lambda), std::forward<Args2>(args)...);
|
|
}
|
|
|
|
//Bind a member function with a shared_ptr object
|
|
template<typename T, typename... Args2>
|
|
void bind_sp(std::shared_ptr<T> pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
DELEGATE_STATIC_ASSERT(!std::is_const<T>::value, "Cannot bind a non-const function on a const object");
|
|
*this = create_sp<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
void bind_sp(std::shared_ptr<T> pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
*this = create_sp<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
//Execute the delegate with the given parameters
|
|
RetVal execute(Args... args) const
|
|
{
|
|
DELEGATE_ASSERT(allocator_.has_allocation(), "Delegate is not bound");
|
|
return ((IDelegateT*)get_delegate())->execute(std::forward<Args>(args)...);
|
|
}
|
|
|
|
RetVal execute_if_bound(Args... args) const
|
|
{
|
|
if (is_bound())
|
|
{
|
|
return ((IDelegateT*)get_delegate())->execute(std::forward<Args>(args)...);
|
|
}
|
|
return RetVal();
|
|
}
|
|
|
|
private:
|
|
template<typename T, typename... Args3>
|
|
void bind(Args3&&... args)
|
|
{
|
|
release();
|
|
void* pAlloc = allocator_.allocate(sizeof(T));
|
|
new (pAlloc) T(std::forward<Args3>(args)...);
|
|
}
|
|
};
|
|
|
|
//Delegate that can be bound to by MULTIPLE objects
|
|
template<typename... Args>
|
|
class multicast_delegate : public delegate_base
|
|
{
|
|
public:
|
|
using DelegateT = delegate<void, Args...>;
|
|
|
|
private:
|
|
struct delegate_handler_pair
|
|
{
|
|
delegate_handle handle;
|
|
DelegateT callback;
|
|
delegate_handler_pair() : handle(false) {}
|
|
delegate_handler_pair(const delegate_handle& handle, const DelegateT& callback) : handle(handle), callback(callback) {}
|
|
delegate_handler_pair(const delegate_handle& handle, DelegateT&& callback) : handle(handle), callback(std::move(callback)) {}
|
|
};
|
|
template<typename T, typename... Args2>
|
|
using ConstMemberFunction = typename _DelegatesInteral::MemberFunction<true, T, void, Args..., Args2...>::Type;
|
|
template<typename T, typename... Args2>
|
|
using NonConstMemberFunction = typename _DelegatesInteral::MemberFunction<false, T, void, Args..., Args2...>::Type;
|
|
|
|
public:
|
|
//Default constructor
|
|
constexpr multicast_delegate()
|
|
: locks_(0)
|
|
{
|
|
}
|
|
|
|
//Default destructor
|
|
~multicast_delegate() noexcept = default;
|
|
|
|
//Default copy constructor
|
|
multicast_delegate(const multicast_delegate& other) = default;
|
|
|
|
//Defaul copy assignment operator
|
|
multicast_delegate& operator=(const multicast_delegate& other) = default;
|
|
|
|
//Move constructor
|
|
multicast_delegate(multicast_delegate&& other) noexcept
|
|
: events_(std::move(other.events_)),
|
|
locks_(std::move(other.locks_))
|
|
{
|
|
}
|
|
|
|
//Move assignment operator
|
|
multicast_delegate& operator=(multicast_delegate&& other) noexcept
|
|
{
|
|
events_ = std::move(other.events_);
|
|
locks_ = std::move(other.locks_);
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
delegate_handle operator+=(T&& l)
|
|
{
|
|
return add(DelegateT::create_lambda(std::move(l)));
|
|
}
|
|
|
|
//Add delegate with the += operator
|
|
delegate_handle operator+=(DelegateT&& handler) noexcept
|
|
{
|
|
return add(std::forward<DelegateT>(handler));
|
|
}
|
|
|
|
//Remove a delegate using its DelegateHandle
|
|
bool operator-=(delegate_handle& handle)
|
|
{
|
|
return remove(handle);
|
|
}
|
|
|
|
delegate_handle add(DelegateT&& handler) noexcept
|
|
{
|
|
//Favour an empty space over a possible array reallocation
|
|
for (size_t i = 0; i < events_.size(); ++i)
|
|
{
|
|
if (events_[i].handle.is_valid() == false)
|
|
{
|
|
events_[i] = delegate_handler_pair(delegate_handle(true), std::move(handler));
|
|
return events_[i].handle;
|
|
}
|
|
}
|
|
events_.emplace_back(delegate_handle(true), std::move(handler));
|
|
return events_.back().handle;
|
|
}
|
|
|
|
//Bind a member function
|
|
template<typename T, typename... Args2>
|
|
delegate_handle add_raw(T* pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::create_raw(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
delegate_handle add_raw(T* pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::create_raw(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
//Bind a static/global function
|
|
template<typename... Args2>
|
|
delegate_handle add_static(void(*pFunction)(Args..., Args2...), Args2&&... args)
|
|
{
|
|
return add(DelegateT::create_static(pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
//Bind a lambda
|
|
template<typename LambdaType, typename... Args2>
|
|
delegate_handle add_lambda(LambdaType&& lambda, Args2&&... args)
|
|
{
|
|
return add(DelegateT::create_lambda(std::forward<LambdaType>(lambda), std::forward<Args2>(args)...));
|
|
}
|
|
|
|
//Bind a member function with a shared_ptr object
|
|
template<typename T, typename... Args2>
|
|
delegate_handle add_sp(std::shared_ptr<T> pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::create_sp(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
delegate_handle add_sp(std::shared_ptr<T> pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::create_sp(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
//Removes all handles that are bound from a specific object
|
|
//Ignored when pObject is null
|
|
//Note: Only works on Raw and SP bindings
|
|
void remove_object(void* pObject)
|
|
{
|
|
if (pObject != nullptr)
|
|
{
|
|
for (size_t i = 0; i < events_.size(); ++i)
|
|
{
|
|
if (events_[i].callback.get_owner() == pObject)
|
|
{
|
|
if (is_locked())
|
|
{
|
|
events_[i].callback.clear();
|
|
}
|
|
else
|
|
{
|
|
std::swap(events_[i], events_[events_.size() - 1]);
|
|
events_.pop_back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Remove a function from the event list by the handle
|
|
bool remove(delegate_handle& handle)
|
|
{
|
|
if (handle.is_valid())
|
|
{
|
|
for (size_t i = 0; i < events_.size(); ++i)
|
|
{
|
|
if (events_[i].handle == handle)
|
|
{
|
|
if (is_locked())
|
|
{
|
|
events_[i].callback.clear();
|
|
}
|
|
else
|
|
{
|
|
std::swap(events_[i], events_[events_.size() - 1]);
|
|
events_.pop_back();
|
|
}
|
|
handle.reset();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool is_bound_to(const delegate_handle& handle) const
|
|
{
|
|
if (handle.is_valid())
|
|
{
|
|
for (size_t i = 0; i < events_.size(); ++i)
|
|
{
|
|
if (events_[i].handle == handle)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//Remove all the functions bound to the delegate
|
|
void remove_all()
|
|
{
|
|
if (is_locked())
|
|
{
|
|
for (delegate_handler_pair& handler : events_)
|
|
{
|
|
handler.callback.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
events_.clear();
|
|
}
|
|
}
|
|
|
|
void compress(const size_t maxSpace = 0)
|
|
{
|
|
if (is_locked() == false)
|
|
{
|
|
size_t toDelete = 0;
|
|
for (size_t i = 0; i < events_.size() - toDelete; ++i)
|
|
{
|
|
if (events_[i].handle.is_valid() == false)
|
|
{
|
|
std::swap(events_[i], events_[toDelete]);
|
|
++toDelete;
|
|
}
|
|
}
|
|
if (toDelete > maxSpace)
|
|
{
|
|
events_.resize(events_.size() - toDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Execute all functions that are bound
|
|
void broadcast(Args ...args)
|
|
{
|
|
lock();
|
|
for (size_t i = 0; i < events_.size(); ++i)
|
|
{
|
|
if (events_[i].handle.is_valid())
|
|
{
|
|
events_[i].callback.execute(std::forward<Args>(args)...);
|
|
}
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
size_t get_size() const
|
|
{
|
|
return events_.size();
|
|
}
|
|
|
|
private:
|
|
void lock()
|
|
{
|
|
++locks_;
|
|
}
|
|
|
|
void unlock()
|
|
{
|
|
//Unlock() should never be called more than Lock()!
|
|
DELEGATE_ASSERT(locks_ > 0);
|
|
--locks_;
|
|
}
|
|
|
|
//Returns true is the delegate is currently broadcasting
|
|
//If this is true, the order of the array should not be changed otherwise this causes undefined behaviour
|
|
bool is_locked() const
|
|
{
|
|
return locks_ > 0;
|
|
}
|
|
|
|
std::vector<delegate_handler_pair> events_;
|
|
unsigned int locks_;
|
|
};
|
|
|
|
#endif
|