Files
Nuake-custom/Nuake/src/Core/MulticastDelegate.h
2024-09-19 21:43:04 +01:00

135 lines
3.5 KiB
C++

#pragma once
#include <functional>
#include <vector>
#define DECLARE_MULTICAST_DELEGATE(multicastDelegateName, ...) typedef MulticastDelegate<__VA_ARGS__> multicastDelegateName;
struct DelegateHandle
{
size_t id = InvalidHandle;
static inline size_t InvalidHandle = static_cast<size_t>(-1);
bool IsValid() const { return id != InvalidHandle; }
void Reset() { id = InvalidHandle; }
// Comparison operators for convenience
bool operator==(const DelegateHandle& other) const { return id == other.id; }
bool operator!=(const DelegateHandle& other) const { return id != other.id; }
};
template<typename... Args>
class MulticastDelegate
{
public:
// Add a callable with bound variables (supports no arguments as well)
template<typename Callable, typename... BoundArgs>
DelegateHandle AddStatic(Callable&& func, BoundArgs&&... boundArgs)
{
size_t id = GetNextID();
auto boundFunction = [=](Args... args) {
if constexpr (sizeof...(Args) > 0)
{
func(boundArgs..., std::forward<Args>(args)...);
}
else
{
func(boundArgs...);
}
};
SetDelegate(id, boundFunction);
return DelegateHandle{ id };
}
template<typename Obj, typename Callable, typename... BoundArgs>
DelegateHandle AddRaw(Obj* object, Callable&& func, BoundArgs&&... boundArgs)
{
size_t id = GetNextID();
auto boundFunction = [=](Args... args) {
if constexpr (sizeof...(Args) > 0)
{
(object->*func)(boundArgs..., std::forward<Args>(args)...);
}
else
{
(object->*func)(boundArgs...);
}
};
SetDelegate(id, boundFunction);
return DelegateHandle{ id };
}
// Remove a callable using the token returned by Add()
void Remove(DelegateHandle& handle)
{
ASSERT(handle.IsValid());
if (handle.IsValid() && handle.id < delegates.size())
{
delegates[handle.id].active = false;
// Mark this slot as reusable
freeIDs.push_back(handle.id);
}
// Invalidate the handle
handle.Reset();
}
// Clear all delegates
void Clear()
{
delegates.clear();
freeIDs.clear();
nextID = 0;
}
// Invoke all callables
void Broadcast(Args... args)
{
for (auto& delegate : delegates)
{
if (delegate.active)
{
delegate.function(std::forward<Args>(args)...);
}
}
}
private:
struct Delegate
{
bool active = false;
std::function<void(Args...)> function;
};
// A vector of delegates with active state
std::vector<Delegate> delegates;
// List of reusable slots
std::vector<size_t> freeIDs;
size_t nextID = 0;
// Get the next available ID, either by reusing a free slot or by creating a new one
size_t GetNextID()
{
if (!freeIDs.empty())
{
size_t id = freeIDs.back();
freeIDs.pop_back();
return id;
}
return nextID++;
}
// Set the delegate in the vector, makes the array larger if necessary
void SetDelegate(size_t id, const std::function<void(Args...)>& func)
{
if (id >= delegates.size())
delegates.resize(id + 1);
delegates[id] = { true, func };
}
};