AronaCore/core/misc/delegates.h

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