Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ BraceWrapping:
BreakAfterAttributes: Always
BreakBeforeTernaryOperators: true
BreakBeforeConceptDeclarations: Always
BreakConstructorInitializers: BeforeColon
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeColon

ColumnLimit: 120
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

# CMake
cmake-build-*/
builds/

# IDE files
.idea
175 changes: 175 additions & 0 deletions app/include/Core/Delegates/Delegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#ifndef TGENGINE_DELEGATE_H
#define TGENGINE_DELEGATE_H

#include "Core/Memory/StackStorage.h"
#include "Core/Types/Callable.h"
#include "DelegateAllocator.h"
#include "ErasedFunctions.h"

#include <optional>

namespace TGEngine::Core
{


template<typename DelegateSignature>
class Delegate;

template<typename RetVal, typename... Args>
class Delegate<RetVal(Args...)>
{
private:

using ErasedDelegate = _DelegateInternals::IDelegate<RetVal(Args...)>;

using Executer = FunctionPtr<RetVal, ErasedDelegate*, Args&&...>;
using Constructor = FunctionPtr<ErasedDelegate*, ErasedDelegate*, const ErasedDelegate*>;

static constexpr size_t storageSize = 64;
static constexpr size_t storageAlign = 8;
using Storage = StackStorage<storageSize, storageAlign>;
using DefaultAllocator = _DelegateInternals::DelegateAllocator<ErasedDelegate, Storage>;

public:

Delegate() = default;

Delegate(const Delegate& delegate)
: defaultAllocator(storage)
, executer(delegate.executer)
, constructor(delegate.constructor)
, erasedDelegate(makeCopy(delegate))
{}

Delegate& operator= (const Delegate& delegate)
{
if (this == &delegate) return *this;
unbind();

executer = delegate.executer;
constructor = delegate.constructor;
erasedDelegate = makeCopy(delegate);

return *this;
}

~Delegate() { unbind(); }

private:

ErasedDelegate* makeCopy(const Delegate& delegate)
{
ErasedDelegate* copy = defaultAllocator.allocateCopy(delegate.storage);
return constructor(copy, delegate.erasedDelegate);
}

public:

[[nodiscard]]
bool isBound() const
{
return erasedDelegate != nullptr;
}

void unbind()
{
using AllocTraits = std::allocator_traits<DefaultAllocator>;

AllocTraits::destroy(defaultAllocator, erasedDelegate);
AllocTraits::deallocate(defaultAllocator, erasedDelegate, 1);
}

public:

[[nodiscard]]
RetVal execute(Args... args) const
{
return executer(erasedDelegate, std::forward<Args>(args)...);
}

[[nodiscard]]
std::optional<RetVal> executeIfBound(Args... args) const
{
if (!isBound()) return std::nullopt;
return executer(erasedDelegate, std::forward<Args>(args)...);
}

public:

template<typename... ErasedArgs>
void bindStatic(FunctionPtr<RetVal, Args..., ErasedArgs...> function, std::decay_t<ErasedArgs>... payload)
{
using namespace _DelegateInternals;
using DelegateType = StaticDelegate<decltype(function), RetVal(Args...), decltype(payload)...>;
bind<DelegateType>(function, std::move(payload)...);
}

template<typename Lambda, typename... Payload>
void bindLambda(Lambda&& lambda, Payload... payload)
requires std::invocable<Lambda, Args..., Payload...>
{
using namespace _DelegateInternals;
using DelegateType = LambdaDelegate<Lambda, RetVal(Args...), Payload...>;
bind<DelegateType>(std::forward<Lambda>(lambda), std::move(payload)...);
}

template<typename T, typename... ErasedArgs>
void bindMethod(NotConstMethodPtr<T, RetVal, Args..., ErasedArgs...> method, T* object,
std::decay_t<ErasedArgs>... payload)
{
constexpr bool isConst = false;
using namespace _DelegateInternals;
using DelegateType = MethodDelegate<decltype(method), T, isConst, RetVal(Args...), decltype(payload)...>;
bind<DelegateType>(method, object, std::move(payload)...);
}

template<typename T, typename... ErasedArgs>
void bindMethod(ConstMethodPtr<T, RetVal, Args..., ErasedArgs...> method, const T* object,
std::decay_t<ErasedArgs>... payload)
{
constexpr bool isConst = true;
using namespace _DelegateInternals;
using DelegateType = MethodDelegate<decltype(method), T, isConst, RetVal(Args...), decltype(payload)...>;
bind<DelegateType>(method, object, std::move(payload)...);
}

private:

template<_DelegateInternals::DelegateType DelegateType, typename... ConstructionArgs>
void bind(ConstructionArgs&&... args)
{
if (isBound()) unbind();

erasedDelegate = makeDelegate<DelegateType>(std::forward<ConstructionArgs>(args)...);
executer = _DelegateInternals::execute<DelegateType>;
constructor = _DelegateInternals::construct<DelegateType>;
}

template<typename DelegateType, typename... ConstructionArgs>
ErasedDelegate* makeDelegate(ConstructionArgs&&... args)
{
using Allocator = typename std::allocator_traits<DefaultAllocator>::template rebind_alloc<DelegateType>;
using AllocTraits = std::allocator_traits<Allocator>;

Allocator allocator(storage);
DelegateType* delegate = AllocTraits::allocate(allocator, 1);
AllocTraits::construct(allocator, delegate, std::forward<ConstructionArgs>(args)...);

return delegate;
}

private:

Storage storage;
DefaultAllocator defaultAllocator {storage};

Executer executer {};
Constructor constructor {};
ErasedDelegate* erasedDelegate {};
};


} // namespace TGEngine::Core


#endif // TGENGINE_DELEGATE_H
79 changes: 79 additions & 0 deletions app/include/Core/Delegates/DelegateAllocator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#ifndef TGENGINE_DELEGATE_ALLOCATOR_H
#define TGENGINE_DELEGATE_ALLOCATOR_H

#include "DelegateTypes.h"

namespace TGEngine::Core::_DelegateInternals
{

template<DelegateType Delegate, typename Storage>
class DelegateAllocator
{
public:

using value_type = Delegate;
using ErasedDelegate = Delegate::Erased;

constexpr explicit DelegateAllocator(Storage& storage)
: storage(storage)
{}

constexpr DelegateAllocator(const DelegateAllocator& other)
: storage(other.storage)
{}

template<typename OtherDelegate>
constexpr explicit DelegateAllocator(const DelegateAllocator<OtherDelegate, Storage>& other)
: storage(other.storage)
{}

template<DelegateType OtherDelegate, typename OStorage>
friend class DelegateAllocator;

template<DelegateType OtherDelegate, typename OStorage>
bool operator== (const DelegateAllocator<OtherDelegate, OStorage>& allocator) const
{
return &storage == &allocator.storage && std::is_base_of_v<ErasedDelegate, OtherDelegate>;
}

public:

[[nodiscard]]
Delegate* allocate(size_t)
{
const size_t bytes = sizeof(Delegate);
return static_cast<Delegate*>(reserve(bytes));
}

[[nodiscard]]
ErasedDelegate* allocateCopy(const Storage& copyStorage)
{
const size_t bytes = copyStorage.used();
return static_cast<ErasedDelegate*>(reserve(bytes));
}

void deallocate(Delegate* delegate, size_t)
{
const size_t used = storage.used();
hasStorageAllocation(delegate) ? storage.deallocate(delegate, used) : std::free(delegate);
}

private:

void* reserve(std::size_t bytes)
{
return canAllocateInStorage(bytes) ? storage.allocate(bytes) : std::aligned_alloc(alignof(Delegate), bytes);
}

bool canAllocateInStorage(size_t bytes) const { return storage.isEmpty() && storage.canAllocate(bytes); }

bool hasStorageAllocation(Delegate* delegate) const { return storage.contains(delegate); }

private:

Storage& storage;
};

} // namespace TGEngine::Core::_DelegateInternals

#endif // TGENGINE_DELEGATE_ALLOCATOR_H
Loading
Loading