mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 14:16:14 +03:00
749 lines
20 KiB
C++
749 lines
20 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <tuple>
|
|
#include <cassert>
|
|
|
|
#include "noncopyable.h"
|
|
#include "cacheline.h"
|
|
#include "allocator.h"
|
|
|
|
#include <functional>
|
|
|
|
// Edit from https://simoncoenen.com/blog/programming/CPP_Delegates
|
|
// We also have eventpp library as another choice.
|
|
namespace engine
|
|
{
|
|
template<typename... Args>
|
|
class CallOnceEvents
|
|
{
|
|
private:
|
|
std::vector<std::function<void(Args...)>> m_collections;
|
|
|
|
public:
|
|
void add(std::function<void(Args...)>&& func)
|
|
{
|
|
m_collections.push_back(func);
|
|
}
|
|
|
|
void brocast(Args&&... args)
|
|
{
|
|
for (auto& func : m_collections)
|
|
{
|
|
func(std::forward<Args>(args)...);
|
|
}
|
|
m_collections.clear();
|
|
}
|
|
};
|
|
|
|
// Basic class for delegate.
|
|
class IDelegateBase
|
|
{
|
|
public:
|
|
IDelegateBase() = default;
|
|
virtual ~IDelegateBase() noexcept = default;
|
|
virtual const void* getOwner() const { return nullptr; }
|
|
virtual void clone(void* pDestination) = 0;
|
|
};
|
|
|
|
// Basic class for delegates with template parameters.
|
|
template<typename RetVal, typename... Args>
|
|
class IDelegate : public IDelegateBase
|
|
{
|
|
public:
|
|
virtual RetVal execute(Args&&... args) = 0;
|
|
};
|
|
|
|
// Forward declare to limit RetVal is a function type, see implement below.
|
|
// Args2 is other type value for payload.
|
|
template<typename RetVal, typename... Args2>
|
|
class StaticDelegate;
|
|
|
|
// Implement with type limit.
|
|
// Args2 is other type value for payload, store in tuple container.
|
|
template<typename RetVal, typename... Args, typename... Args2>
|
|
class StaticDelegate<RetVal(Args...), Args2...> : public IDelegate<RetVal, Args...>
|
|
{
|
|
public:
|
|
using DelegateFunction = RetVal(*)(Args..., Args2...);
|
|
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal executeInternal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
return m_function(std::forward<Args>(args)..., std::get<Is>(m_payload)...);
|
|
}
|
|
|
|
DelegateFunction m_function;
|
|
std::tuple<Args2...> m_payload;
|
|
|
|
public:
|
|
StaticDelegate(DelegateFunction function, Args2&&... payload)
|
|
: m_function(function), m_payload(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
StaticDelegate(DelegateFunction function, const std::tuple<Args2...>& payload)
|
|
: m_function(function), m_payload(payload)
|
|
{}
|
|
|
|
virtual RetVal execute(Args&&... args) override
|
|
{
|
|
return executeInternal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
// We use placement new to optimize memory layout.
|
|
new (pDestination) StaticDelegate(m_function, m_payload);
|
|
}
|
|
};
|
|
|
|
// Same process for other single delegate type.
|
|
namespace DelegatesInteral
|
|
{
|
|
template<bool bConst, 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...);
|
|
};
|
|
}
|
|
|
|
template<bool bConst, typename T, typename RetVal, typename... Args2>
|
|
class RawDelegate;
|
|
|
|
template<bool bConst, typename T, typename RetVal, typename... Args, typename... Args2>
|
|
class RawDelegate<bConst, T, RetVal(Args...), Args2...> : public IDelegate<RetVal, Args...>
|
|
{
|
|
public:
|
|
using DelegateFunction = typename DelegatesInteral::MemberFunction<bConst, T, RetVal, Args..., Args2...>::Type;
|
|
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal executeInternal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
return (m_pObject->*m_function)(std::forward<Args>(args)..., std::get<Is>(m_payload)...);
|
|
}
|
|
|
|
T* m_pObject;
|
|
DelegateFunction m_function;
|
|
std::tuple<Args2...> m_payload;
|
|
|
|
public:
|
|
RawDelegate(T* pObject, DelegateFunction function, Args2&&... payload)
|
|
: m_pObject(pObject), m_function(function), m_payload(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
RawDelegate(T* pObject, DelegateFunction function, const std::tuple<Args2...>& payload)
|
|
: m_pObject(pObject), m_function(function), m_payload(payload)
|
|
{}
|
|
|
|
virtual RetVal execute(Args&&... args) override
|
|
{
|
|
return executeInternal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
virtual const void* getOwner() const override
|
|
{
|
|
return m_pObject;
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
new (pDestination) RawDelegate(m_pObject, m_function, m_payload);
|
|
}
|
|
};
|
|
|
|
template<typename TLambda, typename RetVal, typename... Args>
|
|
class LambdaDelegate;
|
|
|
|
template<typename TLambda, typename RetVal, typename... Args, typename... Args2>
|
|
class LambdaDelegate<TLambda, RetVal(Args...), Args2...> : public IDelegate<RetVal, Args...>
|
|
{
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal executeInternal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
return (RetVal)((m_Lambda)(std::forward<Args>(args)..., std::get<Is>(m_payload)...));
|
|
}
|
|
|
|
TLambda m_Lambda;
|
|
std::tuple<Args2...> m_payload;
|
|
|
|
public:
|
|
explicit LambdaDelegate(TLambda&& lambda, Args2&&... payload)
|
|
: m_Lambda(std::forward<TLambda>(lambda)),
|
|
m_payload(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
explicit LambdaDelegate(const TLambda& lambda, const std::tuple<Args2...>& payload)
|
|
: m_Lambda(lambda),
|
|
m_payload(payload)
|
|
{}
|
|
|
|
RetVal execute(Args&&... args) override
|
|
{
|
|
return executeInternal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
new (pDestination) LambdaDelegate(m_Lambda, m_payload);
|
|
}
|
|
};
|
|
|
|
template<bool bConst, typename T, typename RetVal, typename... Args>
|
|
class SPDelegate;
|
|
|
|
template<bool bConst, typename RetVal, typename T, typename... Args, typename... Args2>
|
|
class SPDelegate<bConst, T, RetVal(Args...), Args2...> : public IDelegate<RetVal, Args...>
|
|
{
|
|
public:
|
|
using DelegateFunction = typename DelegatesInteral::MemberFunction<bConst, T, RetVal, Args..., Args2...>::Type;
|
|
|
|
private:
|
|
template<std::size_t... Is>
|
|
RetVal executeInternal(Args&&... args, std::index_sequence<Is...>)
|
|
{
|
|
if (m_pObject.expired())
|
|
{
|
|
return RetVal();
|
|
}
|
|
else
|
|
{
|
|
std::shared_ptr<T> pPinned = m_pObject.lock();
|
|
return (pPinned.get()->*m_pFunction)(std::forward<Args>(args)..., std::get<Is>(m_payload)...);
|
|
}
|
|
}
|
|
|
|
std::weak_ptr<T> m_pObject;
|
|
DelegateFunction m_pFunction;
|
|
std::tuple<Args2...> m_payload;
|
|
|
|
public:
|
|
SPDelegate(std::shared_ptr<T> pObject, DelegateFunction pFunction, Args2&&... payload)
|
|
: m_pObject(pObject),
|
|
m_pFunction(pFunction),
|
|
m_payload(std::forward<Args2>(payload)...)
|
|
{}
|
|
|
|
SPDelegate(std::weak_ptr<T> pObject, DelegateFunction pFunction, const std::tuple<Args2...>& payload)
|
|
: m_pObject(pObject),
|
|
m_pFunction(pFunction),
|
|
m_payload(payload)
|
|
{}
|
|
|
|
virtual RetVal execute(Args&&... args) override
|
|
{
|
|
return executeInternal(std::forward<Args>(args)..., std::index_sequence_for<Args2...>());
|
|
}
|
|
|
|
virtual const void* getOwner() const override
|
|
{
|
|
return m_pObject.expired() ? nullptr : m_pObject.lock().get();
|
|
}
|
|
|
|
virtual void clone(void* pDestination) override
|
|
{
|
|
new (pDestination) SPDelegate(m_pObject, m_pFunction, m_payload);
|
|
}
|
|
};
|
|
|
|
// Delegate self handle, runtime generate, never repeat.
|
|
class DelegateHandle final
|
|
{
|
|
private:
|
|
using HandleType = uint64_t;
|
|
|
|
// Current handle id.
|
|
HandleType m_id;
|
|
|
|
// Call this function to get never repeat id.
|
|
static HandleType getNewID();
|
|
|
|
// Unvalid id.
|
|
constexpr static const HandleType kInvalidID = HandleType(~0);
|
|
|
|
public:
|
|
// Default handle generate invalid id.
|
|
constexpr DelegateHandle() noexcept : m_id(kInvalidID) { }
|
|
|
|
// explicit handle generate valid id.
|
|
explicit DelegateHandle(bool/*generateId*/) noexcept : m_id(getNewID()) { }
|
|
|
|
~DelegateHandle() noexcept = default;
|
|
|
|
// Default constructor.
|
|
DelegateHandle(const DelegateHandle& other) = default;
|
|
|
|
// Move operator.
|
|
DelegateHandle(DelegateHandle&& other) noexcept : m_id(other.m_id) { other.reset(); }
|
|
|
|
// Copy operator.
|
|
DelegateHandle& operator=(const DelegateHandle& other) = default;
|
|
|
|
// Move copy operator.
|
|
DelegateHandle& operator=(DelegateHandle&& other) noexcept
|
|
{
|
|
m_id = other.m_id;
|
|
other.reset();
|
|
return *this;
|
|
}
|
|
|
|
operator bool() const noexcept { return isValid(); }
|
|
bool isValid() const noexcept { return m_id != kInvalidID; }
|
|
void reset() noexcept { m_id = kInvalidID; }
|
|
|
|
bool operator==(const DelegateHandle& other) const noexcept { return m_id == other.m_id; }
|
|
bool operator<(const DelegateHandle& other) const noexcept { return m_id < other.m_id; }
|
|
};
|
|
|
|
class DelegateBase
|
|
{
|
|
protected:
|
|
SingleInlineAllocator<CPU_CACHELINE_SIZE> m_allocator;
|
|
|
|
IDelegateBase* getDelegate() const
|
|
{
|
|
return static_cast<IDelegateBase*>(m_allocator.getAllocation());
|
|
}
|
|
|
|
void release()
|
|
{
|
|
if (m_allocator.hasAllocation())
|
|
{
|
|
getDelegate()->~IDelegateBase();
|
|
m_allocator.freeInternal();
|
|
}
|
|
}
|
|
|
|
public:
|
|
inline size_t getSize() const { return m_allocator.getSize(); }
|
|
inline bool isBound() const { return m_allocator.hasAllocation(); }
|
|
|
|
bool isBoundTo(void* pObject) const
|
|
{
|
|
if (pObject == nullptr || m_allocator.hasAllocation() == false)
|
|
{
|
|
return false;
|
|
}
|
|
return getDelegate()->getOwner() == pObject;
|
|
}
|
|
|
|
inline void clear()
|
|
{
|
|
release();
|
|
}
|
|
|
|
void clearIfBoundTo(void* pObject)
|
|
{
|
|
if (pObject != nullptr && isBoundTo(pObject))
|
|
{
|
|
release();
|
|
}
|
|
}
|
|
|
|
const void* getOwner() const
|
|
{
|
|
if (m_allocator.hasAllocation())
|
|
{
|
|
return getDelegate()->getOwner();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
public:
|
|
DelegateBase() noexcept
|
|
: m_allocator()
|
|
{}
|
|
|
|
virtual ~DelegateBase() noexcept
|
|
{
|
|
release();
|
|
}
|
|
|
|
DelegateBase(const DelegateBase& other)
|
|
{
|
|
if (other.m_allocator.hasAllocation())
|
|
{
|
|
m_allocator.allocateInternal(other.m_allocator.getSize());
|
|
other.getDelegate()->clone(m_allocator.getAllocation());
|
|
}
|
|
}
|
|
|
|
DelegateBase& operator=(const DelegateBase& other)
|
|
{
|
|
release();
|
|
if (other.m_allocator.hasAllocation())
|
|
{
|
|
m_allocator.allocateInternal(other.m_allocator.getSize());
|
|
other.getDelegate()->clone(m_allocator.getAllocation());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
DelegateBase(DelegateBase&& other) noexcept
|
|
: m_allocator(std::move(other.m_allocator))
|
|
{}
|
|
|
|
DelegateBase& operator=(DelegateBase&& other) noexcept
|
|
{
|
|
release();
|
|
m_allocator = std::move(other.m_allocator);
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
template<typename RetVal, typename... Args>
|
|
class Delegate : public DelegateBase
|
|
{
|
|
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;
|
|
|
|
private:
|
|
template<typename T, typename... Args3>
|
|
void bind(Args3&&... args)
|
|
{
|
|
release();
|
|
void* pAlloc = m_allocator.allocateInternal(sizeof(T));
|
|
new (pAlloc) T(std::forward<Args3>(args)...);
|
|
}
|
|
|
|
public:
|
|
using IDelegateT = IDelegate<RetVal, Args...>;
|
|
|
|
template<typename T, typename... Args2>
|
|
[[nodiscard]] static Delegate createRaw(T* pObj, NonConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
Delegate handler;
|
|
handler.bind<RawDelegate<false, T, RetVal(Args...), Args2...>>(pObj, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
[[nodiscard]] static Delegate createRaw(T* pObj, ConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
Delegate handler;
|
|
handler.bind<RawDelegate<true, T, RetVal(Args...), Args2...>>(pObj, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename... Args2>
|
|
[[nodiscard]] static Delegate createStatic(RetVal(*pFunction)(Args..., Args2...), Args2... args)
|
|
{
|
|
Delegate handler;
|
|
handler.bind<StaticDelegate<RetVal(Args...), Args2...>>(pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
[[nodiscard]] static Delegate createSP(const std::shared_ptr<T>& pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
Delegate handler;
|
|
handler.bind<SPDelegate<false, T, RetVal(Args...), Args2...>>(pObject, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
[[nodiscard]] static Delegate createSP(const std::shared_ptr<T>& pObject, ConstMemberFunction<T, Args2...> pFunction, Args2... args)
|
|
{
|
|
Delegate handler;
|
|
handler.bind<SPDelegate<true, T, RetVal(Args...), Args2...>>(pObject, pFunction, std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename TLambda, typename... Args2>
|
|
[[nodiscard]] static Delegate createLambda(TLambda&& lambda, Args2... args)
|
|
{
|
|
Delegate handler;
|
|
handler.bind<LambdaDelegate<TLambda, RetVal(Args...), Args2...>>(std::forward<TLambda>(lambda), std::forward<Args2>(args)...);
|
|
return handler;
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
void bindRaw(T* pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
static_assert(!std::is_const<T>::value, "Cannot bind a non-const function on a const object");
|
|
*this = createRaw<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
void bindRaw(T* pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
*this = createRaw<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
template<typename... Args2>
|
|
void bindStatic(RetVal(*pFunction)(Args..., Args2...), Args2&&... args)
|
|
{
|
|
*this = createStatic<Args2... >(pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
template<typename LambdaType, typename... Args2>
|
|
void bindLambda(LambdaType&& lambda, Args2&&... args)
|
|
{
|
|
*this = createLambda<LambdaType, Args2... >(std::forward<LambdaType>(lambda), std::forward<Args2>(args)...);
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
void bindSP(std::shared_ptr<T> pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
static_assert(!std::is_const<T>::value, "Cannot bind a non-const function on a const object");
|
|
*this = createSP<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
void bindSP(std::shared_ptr<T> pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
*this = createSP<T, Args2... >(pObject, pFunction, std::forward<Args2>(args)...);
|
|
}
|
|
|
|
RetVal execute(Args... args) const
|
|
{
|
|
assert(m_allocator.hasAllocation() && "Delegate is not bound");
|
|
return ((IDelegateT*)getDelegate())->execute(std::forward<Args>(args)...);
|
|
}
|
|
|
|
RetVal executeIfBound(Args... args) const
|
|
{
|
|
if (isBound())
|
|
{
|
|
return ((IDelegateT*)getDelegate())->execute(std::forward<Args>(args)...);
|
|
}
|
|
return RetVal();
|
|
}
|
|
};
|
|
|
|
template<typename... Args>
|
|
class MulticastDelegate : public DelegateBase
|
|
{
|
|
public:
|
|
using DelegateT = Delegate<void, Args...>;
|
|
|
|
private:
|
|
struct DelegateHandlerPair
|
|
{
|
|
DelegateHandle handle;
|
|
DelegateT callback;
|
|
|
|
DelegateHandlerPair() : handle(false) {}
|
|
DelegateHandlerPair(const DelegateHandle& handle, const DelegateT& callback) : handle(handle), callback(callback) {}
|
|
DelegateHandlerPair(const DelegateHandle& 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;
|
|
|
|
private:
|
|
std::vector<DelegateHandlerPair> m_events;
|
|
unsigned int m_lock;
|
|
|
|
inline void lock() { ++m_lock; }
|
|
inline void unlock(){ assert(m_lock > 0); --m_lock; }
|
|
inline bool isLocked() const { return m_lock > 0; }
|
|
|
|
public:
|
|
inline size_t getSize() const { return m_events.size(); }
|
|
|
|
public:
|
|
constexpr MulticastDelegate() : m_lock(0) { }
|
|
~MulticastDelegate() noexcept = default;
|
|
MulticastDelegate(const MulticastDelegate& other) = default;
|
|
MulticastDelegate& operator=(const MulticastDelegate& other) = default;
|
|
|
|
MulticastDelegate(MulticastDelegate&& other) noexcept
|
|
: m_events(std::move(other.m_events)), m_lock(std::move(other.m_lock))
|
|
{
|
|
|
|
}
|
|
|
|
MulticastDelegate& operator=(MulticastDelegate&& other) noexcept
|
|
{
|
|
m_events = std::move(other.m_events);
|
|
m_lock = std::move(other.m_lock);
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
DelegateHandle operator+=(T&& l) { return add(DelegateT::createLambda(std::move(l))); }
|
|
|
|
DelegateHandle operator+=(DelegateT&& handler) noexcept { return add(std::forward<DelegateT>(handler)); }
|
|
bool operator-=(DelegateHandle& handle) { return remove(handle); }
|
|
|
|
template<typename T, typename... Args2>
|
|
DelegateHandle addRaw(T* pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::createRaw(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
DelegateHandle addRaw(T* pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::createRaw(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
template<typename... Args2>
|
|
DelegateHandle addStatic(void(*pFunction)(Args..., Args2...), Args2&&... args)
|
|
{
|
|
return add(DelegateT::createStatic(pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
template<typename LambdaType, typename... Args2>
|
|
DelegateHandle addLambda(LambdaType&& lambda, Args2&&... args)
|
|
{
|
|
return add(DelegateT::createLambda(std::forward<LambdaType>(lambda), std::forward<Args2>(args)...));
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
DelegateHandle addSP(std::shared_ptr<T> pObject, NonConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::createSP(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
template<typename T, typename... Args2>
|
|
DelegateHandle addSP(std::shared_ptr<T> pObject, ConstMemberFunction<T, Args2...> pFunction, Args2&&... args)
|
|
{
|
|
return add(DelegateT::createSP(pObject, pFunction, std::forward<Args2>(args)...));
|
|
}
|
|
|
|
public:
|
|
DelegateHandle add(DelegateT&& handler) noexcept
|
|
{
|
|
for (size_t i = 0; i < m_events.size(); ++i)
|
|
{
|
|
if (m_events[i].handle.isValid() == false)
|
|
{
|
|
m_events[i] = DelegateHandlerPair(DelegateHandle(true), std::move(handler));
|
|
return m_events[i].handle;
|
|
}
|
|
}
|
|
m_events.emplace_back(DelegateHandle(true), std::move(handler));
|
|
return m_events.back().handle;
|
|
}
|
|
|
|
void removeObject(void* pObject)
|
|
{
|
|
if (pObject != nullptr)
|
|
{
|
|
for (size_t i = 0; i < m_events.size(); ++i)
|
|
{
|
|
if (m_events[i].callback.getOwner() == pObject)
|
|
{
|
|
if (isLocked())
|
|
{
|
|
m_events[i].callback.clear();
|
|
}
|
|
else
|
|
{
|
|
std::swap(m_events[i], m_events[m_events.size() - 1]);
|
|
m_events.pop_back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool remove(DelegateHandle& handle)
|
|
{
|
|
if (handle.isValid())
|
|
{
|
|
for (size_t i = 0; i < m_events.size(); ++i)
|
|
{
|
|
if (m_events[i].handle == handle)
|
|
{
|
|
if (isLocked())
|
|
{
|
|
m_events[i].callback.clear();
|
|
}
|
|
else
|
|
{
|
|
std::swap(m_events[i], m_events[m_events.size() - 1]);
|
|
m_events.pop_back();
|
|
}
|
|
handle.reset();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isBoundTo(const DelegateHandle& handle) const
|
|
{
|
|
if (handle.isValid())
|
|
{
|
|
for (size_t i = 0; i < m_events.size(); ++i)
|
|
{
|
|
if (m_events[i].handle == handle)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void removeAll()
|
|
{
|
|
if (isLocked())
|
|
{
|
|
for (DelegateHandlerPair& handler : m_events)
|
|
{
|
|
handler.callback.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_events.clear();
|
|
}
|
|
}
|
|
|
|
void shrink(const size_t maxSpace = 0)
|
|
{
|
|
if (isLocked() == false)
|
|
{
|
|
size_t toDelete = 0;
|
|
for (size_t i = 0; i < m_events.size() - toDelete; ++i)
|
|
{
|
|
if (m_events[i].handle.isValid() == false)
|
|
{
|
|
std::swap(m_events[i], m_events[toDelete]);
|
|
++toDelete;
|
|
}
|
|
}
|
|
if (toDelete > maxSpace)
|
|
{
|
|
m_events.resize(m_events.size() - toDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
void broadcast(Args ...args)
|
|
{
|
|
lock();
|
|
for (size_t i = 0; i < m_events.size(); ++i)
|
|
{
|
|
if (m_events[i].handle.isValid())
|
|
{
|
|
m_events[i].callback.execute(std::forward<Args>(args)...);
|
|
}
|
|
}
|
|
unlock();
|
|
}
|
|
};
|
|
} |