/* 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