From 4d7f1c30fea8974d4dee0acc58eadb5f89ee7b7c Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 13 Jan 2024 14:55:26 +0300 Subject: [PATCH 01/16] Begin implement delegates --- app/src/main.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/app/src/main.cpp b/app/src/main.cpp index 5bc5493..761ae0f 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -1,4 +1,111 @@ +#include +#include + +template +constexpr std::string_view type_name() +{ + using namespace std; +#ifdef __clang__ + string_view p = __PRETTY_FUNCTION__; + return string_view(p.data() + 34, p.size() - 34 - 1); +#elif defined(__GNUC__) + string_view p = __PRETTY_FUNCTION__; +#if __cplusplus < 201'402 + return string_view(p.data() + 36, p.size() - 36 - 1); +#else + return string_view(p.data() + 49, p.find(';', 49) - 49); +#endif +#elif defined(_MSC_VER) + string_view p = __FUNCSIG__; + return string_view(p.data() + 84, p.size() - 84 - 7); +#endif + return std::string_view {}; +} + +struct A +{ + A() = default; + + A(const A&) { std::cout << "Copy constructor A\n"; } + + A(A&&) noexcept { std::cout << "Move constructor A\n"; } + + A& operator= (const A&) + { + std::cout << "Copy assigment A\n"; + return *this; + } + + A& operator= (A&&) noexcept + { + std::cout << "Move assigment A\n"; + return *this; + } +}; + +class IDelegate +{ +public: + + template + requires std::invocable + auto execute(Func&& func, Args&&... args) + { + return std::invoke(std::forward(func), std::forward(args)...); + } + + virtual ~IDelegate() = default; +}; + +template +class LambdaDelegate: public IDelegate +{ +public: + + template + explicit LambdaDelegate(TLambda&& lambda, TPayload&&... payload) + : func(std::forward(lambda)), + payload(std::forward(payload)...) + {} + +private: + + Lambda func; + std::tuple payload; +}; + +template +explicit LambdaDelegate(Lambda&& lambda, Payload&&... payload) + -> LambdaDelegate, std::remove_reference_t...>; + +template +struct Test +{ + explicit Test(T t) : t(t) {}; + T t; +}; + +template +struct Test +{ +}; + +std::string foo(int, double&, char&&); + int main() { + A aa; + + LambdaDelegate ld1([aa]() { return 1; }); + + auto l = [aa]() { return 2; }; + LambdaDelegate ld2(l, 1); + + std::cout << type_name() << "\n"; + std::cout << type_name() << "\n"; + + Test t; + std::cout << type_name() << "\n"; + return 0; } \ No newline at end of file From 454d8b6e3b5f6650d054037f77149dcd1dda1a66 Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 16 Mar 2024 15:17:13 +0300 Subject: [PATCH 02/16] Add aliases for callable types --- app/include/Core/Types/Callable.h | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/include/Core/Types/Callable.h diff --git a/app/include/Core/Types/Callable.h b/app/include/Core/Types/Callable.h new file mode 100644 index 0000000..3f8ba97 --- /dev/null +++ b/app/include/Core/Types/Callable.h @@ -0,0 +1,39 @@ +#ifndef TGENGINE_CALLABLE_H +#define TGENGINE_CALLABLE_H + +namespace TGEngine::Types +{ + +namespace internal +{ + +template +struct MethodDeductionType +{ + using Type = RetVal (T::*)(Args...); +}; + +template +struct MethodDeductionType +{ + using Type = RetVal (T::*)(Args...) const; +}; + +} // namespace internal + +template +using MethodPtr = typename internal::MethodDeductionType::Type; + +template +using NotConstMethodPtr = RetVal (T::*)(Args...); + +template +using ConstMethodPtr = RetVal (T::*)(Args...) const; + +template +using FunctionPtr = RetVal (*)(Args...); + +} // namespace TGEngine::Types + + +#endif // TGENGINE_CALLABLE_H From 17142862fd8de053c76a2bf349dfde6833846dd3 Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 16 Mar 2024 15:20:26 +0300 Subject: [PATCH 03/16] Add wrappers for callable types, e.g functions, lamdbas and methods --- app/include/Core/Delegates/DelegateTypes.h | 166 +++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 app/include/Core/Delegates/DelegateTypes.h diff --git a/app/include/Core/Delegates/DelegateTypes.h b/app/include/Core/Delegates/DelegateTypes.h new file mode 100644 index 0000000..0afa10f --- /dev/null +++ b/app/include/Core/Delegates/DelegateTypes.h @@ -0,0 +1,166 @@ +#ifndef TGENGINE_DELEGATE_TYPES_H +#define TGENGINE_DELEGATE_TYPES_H + +#include "Core/Types/Callable.h" + +#include +#include +#include + +template +constexpr std::string_view type_name() +{ + using namespace std; +#ifdef __clang__ + string_view p = __PRETTY_FUNCTION__; + return string_view(p.data() + 34, p.size() - 34 - 1); +#elif defined(__GNUC__) + string_view p = __PRETTY_FUNCTION__; +#if __cplusplus < 201'402 + return string_view(p.data() + 36, p.size() - 36 - 1); +#else + return string_view(p.data() + 49, p.find(';', 49) - 49); +#endif +#elif defined(_MSC_VER) + string_view p = __FUNCSIG__; + return string_view(p.data() + 84, p.size() - 84 - 7); +#endif + return std::string_view {}; +} + +namespace TGEngine::Core +{ + +template +class IDelegate +{ +public: + + virtual ~IDelegate() = default; +}; + + +template +class StaticDelegate; + +template +class StaticDelegate: public IDelegate +{ +public: + + constexpr explicit StaticDelegate(Function function, Payload&&... payload) + : function(function) + , payload(std::forward(payload)...) + { + std::cout << "\nStaticDelegate\n"; + std::cout << type_namefunction)>() << "\n"; + std::cout << type_namepayload)>() << "\n"; + } + + [[nodiscard]] + constexpr RetVal execute(Args&&... args) + { + return executeWithPayload(std::forward(args)..., std::index_sequence_for {}); + } + +private: + + template + [[nodiscard]] + constexpr RetVal executeWithPayload(Args&&... args, std::index_sequence) + { + return function(std::forward(args)..., std::get(payload)...); + } + +private: + + Function function; + std::tuple payload; +}; + + +template +class LambdaDelegate; + +template +class LambdaDelegate: public IDelegate +{ +public: + + constexpr explicit LambdaDelegate(Lambda&& lambda, Payload&&... payload) + : lambda(std::forward(lambda)) + , payload(std::forward(payload)...) + { + std::cout << "\nLambdaDelegate\n"; + std::cout << type_namelambda)>() << "\n"; + std::cout << type_namepayload)>() << "\n"; + } + + [[nodiscard]] + constexpr RetVal execute(Args&&... args) + { + return executeWithPayload(std::forward(args)..., std::index_sequence_for {}); + } + +private: + + template + [[nodiscard]] + constexpr RetVal executeWithPayload(Args&&... args, std::index_sequence) + { + return lambda(std::forward(args)..., std::get(payload)...); + } + +private: + + std::remove_reference_t lambda; + std::tuple payload; +}; + + +template +class MethodDelegate; + +template +class MethodDelegate: public IDelegate +{ +public: + + using Object = std::conditional_t; + + constexpr explicit MethodDelegate(Method method, Object object, Payload&&... payload) + : method(method) + , object(object) + , payload(std::forward(payload)...) + { + std::cout << "\nMethodDelegate\n"; + std::cout << type_namemethod)>() << "\n"; + std::cout << type_nameobject)>() << "\n"; + std::cout << type_namepayload)>() << "\n"; + } + + [[nodiscard]] + constexpr RetVal execute(Args&&... args) + { + return executeWithPayload(std::forward(args)..., std::index_sequence_for {}); + } + +private: + + template + [[nodiscard]] + constexpr RetVal executeWithPayload(Args&&... args, std::index_sequence) + { + return (object->*method)(std::forward(args)..., std::get(payload)...); + } + +private: + + Method method; + Object object; + std::tuple payload; +}; + +} // namespace TGEngine::Core + +#endif // TGENGINE_DELEGATE_TYPES_H From 44356eb2b38c6d762691769bf3161be7f23efeaa Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 16 Mar 2024 15:21:59 +0300 Subject: [PATCH 04/16] Add Binder class for binding any callable type to it and delegate class for execution --- app/include/Core/Delegates/Delegate.h | 170 ++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 app/include/Core/Delegates/Delegate.h diff --git a/app/include/Core/Delegates/Delegate.h b/app/include/Core/Delegates/Delegate.h new file mode 100644 index 0000000..45d65de --- /dev/null +++ b/app/include/Core/Delegates/Delegate.h @@ -0,0 +1,170 @@ +#ifndef DELEGATE_H +#define DELEGATE_H + +#include "DelegateAllocator.h" +#include "DelegateTypes.h" + +#include +#include +#include + + +namespace TGEngine::Core +{ + +template +concept Invocable = requires(Function func, Args... args) { + { + func(args...) + } -> std::same_as; +}; + +template +class Binder; + +template +class Binder +{ +public: + + Binder() + : executer(nullptr) + , delegate(nullptr) + {} + + Binder(const Binder&) = delete; + Binder& operator= (const Binder&) = delete; + + Binder(Binder&& otherBinder) noexcept + : executer(otherBinder.executer) + , delegate(otherBinder.delegate) + { + otherBinder.executer = nullptr; + otherBinder.delegate = nullptr; + } + + Binder& operator= (Binder&& otherBinder) noexcept + { + Binder binder = std::move(otherBinder); + swap(binder); + + return *this; + } + + ~Binder() { unbind(); } + +private: + + void swap(Binder& binder) noexcept + { + std::swap(executer, binder.executer); + std::swap(delegate, binder.delegate); + } + +public: + + template + void bindStatic(Types::FunctionPtr function, std::decay_t... payload) + { + using DelegateType = StaticDelegate; + bind(function, std::move(payload)...); + } + + template + void bindLambda(Lambda&& lambda, Payload... payload) + requires Invocable + { + using DelegateType = LambdaDelegate; + bind(std::forward(lambda), std::move(payload)...); + } + + template + void bindMethod(Types::NotConstMethodPtr method, T* object, + std::decay_t... payload) + { + constexpr bool IsConst = false; + using DelegateType = MethodDelegate; + bind(method, object, std::move(payload)...); + } + + template + void bindMethod(Types::ConstMethodPtr method, const T* object, + std::decay_t... payload) + { + constexpr bool IsConst = true; + using DelegateType = MethodDelegate; + bind(method, object, std::move(payload)...); + } + +private: + + template + void bind(Callable&& callable, ConstructionArgs&&... args) + { + if (isBound()) unbind(); + executer = execute; + delegate = new DelegateType(std::forward(callable), std::forward(args)...); + } + +public: + + [[nodiscard]] + bool isBound() const + { + return delegate != nullptr; + } + + void unbind() const + { + /// + delete delegate; + } + +private: + + using ErasedDelegate = IDelegate*; + + template + constexpr static RetVal execute(ErasedDelegate delegate, Args&&... args) + { + return static_cast(*delegate).execute(std::forward(args)...); + } + +protected: + + using Executer = Types::FunctionPtr; + + Executer executer; + ErasedDelegate delegate; +}; + + +template +class Delegate; + +template +class Delegate final: public Binder +{ +public: + + using Super = Binder; + + [[nodiscard]] + RetVal execute(Args... args) const + { + return Super::executer(Super::delegate, std::forward(args)...); + } + + [[nodiscard]] + std::optional executeIfBound(Args... args) const + { + if (!Super::isBound()) return std::nullopt; + return Super::executer(Super::delegate, std::forward(args)...); + } +}; + + +} // namespace TGEngine::Core + + +#endif // DELEGATE_H From f23e7125d8900cb7bdceaca8a8dc4b371c451f48 Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 16 Mar 2024 15:22:42 +0300 Subject: [PATCH 05/16] Start implement custom allocator for delegates --- .../Core/Delegates/DelegateAllocator.h | 42 +++++++++ app/include/Core/Memory/StackAllocator.h | 94 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 app/include/Core/Delegates/DelegateAllocator.h create mode 100644 app/include/Core/Memory/StackAllocator.h diff --git a/app/include/Core/Delegates/DelegateAllocator.h b/app/include/Core/Delegates/DelegateAllocator.h new file mode 100644 index 0000000..aad3b86 --- /dev/null +++ b/app/include/Core/Delegates/DelegateAllocator.h @@ -0,0 +1,42 @@ +#ifndef DELEGATEALLOCATOR_H +#define DELEGATEALLOCATOR_H + +#include "Core/Memory/StackAllocator.h" +#include "DelegateTypes.h" + +namespace TGEngine::Core +{ + +template +class DelegateAllocator +{ +public: + + inline static constexpr size_t size = 32; + inline static constexpr size_t align = 8; + + using Storage = StackStorage; + + DelegateAllocator() + : storage() + {} + + using ErasedDelegate = IDelegate*; + + ErasedDelegate allocate(size_t n) + { + if (storage.used() + n < size) + { + return storage.push(n); + } + + } + +private: + + Storage storage; +}; + +} // namespace TGEngine::Core + +#endif // DELEGATEALLOCATOR_H diff --git a/app/include/Core/Memory/StackAllocator.h b/app/include/Core/Memory/StackAllocator.h new file mode 100644 index 0000000..224e625 --- /dev/null +++ b/app/include/Core/Memory/StackAllocator.h @@ -0,0 +1,94 @@ +#ifndef STACKALLOCATOR_H +#define STACKALLOCATOR_H + +#include +#include +#include +#include + +template +class StackStorage +{ +public: + + constexpr StackStorage() + : buffer() + , ptr(buffer) + {} + + StackStorage(const StackStorage&) = delete; + StackStorage& operator= (const StackStorage&) = delete; + + explicit operator std::byte* () { return buffer; } + + [[nodiscard]] + void* push(size_t bytes) + { + void* currentPtr = ptr; + ptr += bytes; + return currentPtr; + } + + void pop(void* p, size_t bytes) + { + assert(p != ptr - bytes); + ptr -= bytes; + } + + [[nodiscard]] + size_t used() const + { + return ptr - buffer; + } + +private: + + alignas(Align) std::array buffer; + std::byte* ptr; +}; + +template +class StackAllocator: private AllocHelper +{ +public: + + using value_type = T; + + using Storage = StackStorage; + + explicit StackAllocator(Storage& storage) + : storage(storage) + , ptr(storage) + {} + + T* allocate(size_t n) + { + const size_t alignedSize = alignUp(n); + if (used() > N - alignedSize) return AllocHelper::allocate(n); + + std::byte* result = ptr; + ptr += alignedSize; + return reinterpret_cast(result); + } + + void deallocate(T* p, size_t n) + { + if (!storage.contains(p)) AllocHelper::deallocate(p, n); + + const size_t alignedSize = alignUp(n); + assert(ptr - p == alignedSize); + } + + size_t used() { return ptr - static_cast(storage); } + +private: + + static size_t alignUp(size_t n) { return (n * sizeof(T) + Align - 1) & ~(Align - 1); } + +private: + + Storage& storage; + std::byte* ptr; +}; + +#endif // STACKALLOCATOR_H From c079730a1d33bdf70bcefa99c214d3464359854d Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 20 Apr 2024 23:32:01 +0300 Subject: [PATCH 06/16] Implement stack storage class --- app/include/Core/Memory/StackAllocator.h | 94 ------------------------ app/include/Core/Memory/StackStorage.h | 67 +++++++++++++++++ 2 files changed, 67 insertions(+), 94 deletions(-) delete mode 100644 app/include/Core/Memory/StackAllocator.h create mode 100644 app/include/Core/Memory/StackStorage.h diff --git a/app/include/Core/Memory/StackAllocator.h b/app/include/Core/Memory/StackAllocator.h deleted file mode 100644 index 224e625..0000000 --- a/app/include/Core/Memory/StackAllocator.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef STACKALLOCATOR_H -#define STACKALLOCATOR_H - -#include -#include -#include -#include - -template -class StackStorage -{ -public: - - constexpr StackStorage() - : buffer() - , ptr(buffer) - {} - - StackStorage(const StackStorage&) = delete; - StackStorage& operator= (const StackStorage&) = delete; - - explicit operator std::byte* () { return buffer; } - - [[nodiscard]] - void* push(size_t bytes) - { - void* currentPtr = ptr; - ptr += bytes; - return currentPtr; - } - - void pop(void* p, size_t bytes) - { - assert(p != ptr - bytes); - ptr -= bytes; - } - - [[nodiscard]] - size_t used() const - { - return ptr - buffer; - } - -private: - - alignas(Align) std::array buffer; - std::byte* ptr; -}; - -template -class StackAllocator: private AllocHelper -{ -public: - - using value_type = T; - - using Storage = StackStorage; - - explicit StackAllocator(Storage& storage) - : storage(storage) - , ptr(storage) - {} - - T* allocate(size_t n) - { - const size_t alignedSize = alignUp(n); - if (used() > N - alignedSize) return AllocHelper::allocate(n); - - std::byte* result = ptr; - ptr += alignedSize; - return reinterpret_cast(result); - } - - void deallocate(T* p, size_t n) - { - if (!storage.contains(p)) AllocHelper::deallocate(p, n); - - const size_t alignedSize = alignUp(n); - assert(ptr - p == alignedSize); - } - - size_t used() { return ptr - static_cast(storage); } - -private: - - static size_t alignUp(size_t n) { return (n * sizeof(T) + Align - 1) & ~(Align - 1); } - -private: - - Storage& storage; - std::byte* ptr; -}; - -#endif // STACKALLOCATOR_H diff --git a/app/include/Core/Memory/StackStorage.h b/app/include/Core/Memory/StackStorage.h new file mode 100644 index 0000000..be223bb --- /dev/null +++ b/app/include/Core/Memory/StackStorage.h @@ -0,0 +1,67 @@ +#ifndef STACKALLOCATOR_H +#define STACKALLOCATOR_H + +#include +#include +#include +#include + + +template +class StackStorage +{ +public: + + constexpr StackStorage() + : buffer() + , ptr(buffer.data()) + {} + + StackStorage(const StackStorage&) = delete; + StackStorage& operator= (const StackStorage&) = delete; + + static constexpr size_t size = N; + static constexpr size_t align = Align; + +public: + + [[nodiscard]] + void* push(size_t bytes) + { + void* currentPtr = ptr; + ptr += bytes; + return currentPtr; + } + + void pop(const void* p, size_t bytes) + { + assert(p != ptr - bytes); + ptr -= bytes; + } + + [[nodiscard]] + bool contains(const void* p) const + { + return p && p >= buffer.cbegin() && p < buffer.cend(); + } + + [[nodiscard]] + size_t used() const + { + return std::distance(ptr, buffer.data()); + } + + [[nodiscard]] + bool isEmpty() const + { + return ptr == buffer.cbegin(); + } + +private: + + alignas(Align) std::array buffer; + std::byte* ptr; +}; + + +#endif // STACKALLOCATOR_H From 9ccf1d5aa6d312cf97c01a0c893585ccd4623605 Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 20 Apr 2024 23:32:15 +0300 Subject: [PATCH 07/16] Implement allocator for delegates --- .../Core/Delegates/DelegateAllocator.h | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/app/include/Core/Delegates/DelegateAllocator.h b/app/include/Core/Delegates/DelegateAllocator.h index aad3b86..18323fa 100644 --- a/app/include/Core/Delegates/DelegateAllocator.h +++ b/app/include/Core/Delegates/DelegateAllocator.h @@ -1,13 +1,13 @@ #ifndef DELEGATEALLOCATOR_H #define DELEGATEALLOCATOR_H -#include "Core/Memory/StackAllocator.h" +#include "Core/Memory/StackStorage.h" #include "DelegateTypes.h" namespace TGEngine::Core { -template +template class DelegateAllocator { public: @@ -17,24 +17,40 @@ class DelegateAllocator using Storage = StackStorage; - DelegateAllocator() - : storage() + using value_type = DelegateType; + + explicit DelegateAllocator(Storage& storage) + : storage(storage) {} - using ErasedDelegate = IDelegate*; + DelegateType* allocate(size_t n) + { + const size_t bytes = n * sizeof(DelegateType); + void* ptr = canAllocateInStorage(bytes) ? storage.push(bytes) : std::malloc(bytes); + return static_cast(ptr); + } + + void deallocate(DelegateType* delegate, size_t) + { + const size_t used = storage.used(); + hasStorageAllocation(delegate) ? storage.pop(delegate, used) : std::free(delegate); + } - ErasedDelegate allocate(size_t n) + [[nodiscard]] + bool canAllocateInStorage(size_t bytes) const { - if (storage.used() + n < size) - { - return storage.push(n); - } + return storage.used() + bytes <= size && storage.isEmpty(); + } + [[nodiscard]] + bool hasStorageAllocation(DelegateType* delegate) const + { + return storage.contains(delegate); } private: - Storage storage; + Storage& storage; }; } // namespace TGEngine::Core From 69f3e4c8254459795026cd4612b2d6f475e660ea Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 20 Apr 2024 23:32:57 +0300 Subject: [PATCH 08/16] Add delegate allocator to delegates implementation --- app/include/Core/Delegates/Delegate.h | 48 ++++++++++++++-------- app/include/Core/Delegates/DelegateTypes.h | 11 +++-- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/app/include/Core/Delegates/Delegate.h b/app/include/Core/Delegates/Delegate.h index 45d65de..b8d1be1 100644 --- a/app/include/Core/Delegates/Delegate.h +++ b/app/include/Core/Delegates/Delegate.h @@ -8,7 +8,6 @@ #include #include - namespace TGEngine::Core { @@ -19,18 +18,22 @@ concept Invocable = requires(Function func, Args... args) { } -> std::same_as; }; -template +template>> class Binder; -template -class Binder +template +class Binder { public: Binder() : executer(nullptr) , delegate(nullptr) - {} + , storage() + , allocator(storage) + { + std::cout << type_name() << "\n"; + } Binder(const Binder&) = delete; Binder& operator= (const Binder&) = delete; @@ -101,9 +104,16 @@ class Binder template void bind(Callable&& callable, ConstructionArgs&&... args) { + using DelegateTypeAllocator = typename std::allocator_traits::template rebind_alloc; + if (isBound()) unbind(); + + DelegateTypeAllocator delegateTypeAllocator(storage); + auto specificDelegate = std::allocator_traits::allocate(delegateTypeAllocator, 1); + std::allocator_traits::construct(allocator, specificDelegate, std::forward(callable), + std::forward(args)...); + delegate = specificDelegate; executer = execute; - delegate = new DelegateType(std::forward(callable), std::forward(args)...); } public: @@ -114,40 +124,44 @@ class Binder return delegate != nullptr; } - void unbind() const + void unbind() { - /// - delete delegate; + std::allocator_traits::destroy(allocator, delegate); + std::allocator_traits::deallocate(allocator, delegate, 1); } private: - using ErasedDelegate = IDelegate*; + using ErasedDelegate = IDelegate; template - constexpr static RetVal execute(ErasedDelegate delegate, Args&&... args) + constexpr static RetVal execute(ErasedDelegate* delegate, Args&&... args) { + assert(delegate); return static_cast(*delegate).execute(std::forward(args)...); } protected: - using Executer = Types::FunctionPtr; + using Executer = Types::FunctionPtr; Executer executer; - ErasedDelegate delegate; + ErasedDelegate* delegate; + + StackStorage<32, 8> storage; + Allocator allocator; }; -template +template>> class Delegate; -template -class Delegate final: public Binder +template +class Delegate final: public Binder { public: - using Super = Binder; + using Super = Binder; [[nodiscard]] RetVal execute(Args... args) const diff --git a/app/include/Core/Delegates/DelegateTypes.h b/app/include/Core/Delegates/DelegateTypes.h index 0afa10f..d50ce56 100644 --- a/app/include/Core/Delegates/DelegateTypes.h +++ b/app/include/Core/Delegates/DelegateTypes.h @@ -31,8 +31,11 @@ constexpr std::string_view type_name() namespace TGEngine::Core { +template +class IDelegate; + template -class IDelegate +class IDelegate { public: @@ -44,7 +47,7 @@ template class StaticDelegate; template -class StaticDelegate: public IDelegate +class StaticDelegate: public IDelegate { public: @@ -83,7 +86,7 @@ template class LambdaDelegate; template -class LambdaDelegate: public IDelegate +class LambdaDelegate: public IDelegate { public: @@ -122,7 +125,7 @@ template -class MethodDelegate: public IDelegate +class MethodDelegate: public IDelegate { public: From a7c8d6fa25f97f96c85405cf79c02b1cdbb56fb9 Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 20 Apr 2024 23:33:21 +0300 Subject: [PATCH 09/16] Update gitignore and clang-format --- .clang-format | 2 +- .gitignore | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 697d44e..2ede1e2 100644 --- a/.clang-format +++ b/.clang-format @@ -35,7 +35,7 @@ BraceWrapping: BreakAfterAttributes: Always BreakBeforeTernaryOperators: true BreakBeforeConceptDeclarations: Always -BreakConstructorInitializers: BeforeColon +BreakConstructorInitializers: BeforeComma BreakInheritanceList: BeforeColon ColumnLimit: 120 diff --git a/.gitignore b/.gitignore index ef5bbe8..e10868d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ # CMake cmake-build-*/ +builds/ # IDE files .idea \ No newline at end of file From 234dbf1b1149ea6d655f81bb0952972750418936 Mon Sep 17 00:00:00 2001 From: Nomlsbad Date: Sat, 20 Apr 2024 23:33:38 +0300 Subject: [PATCH 10/16] Edit main --- app/src/main.cpp | 98 +++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 71 deletions(-) diff --git a/app/src/main.cpp b/app/src/main.cpp index 761ae0f..d3f699a 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -1,26 +1,8 @@ -#include -#include +#include "Core/Delegates/Delegate.h" +#include "Core/Memory/StackStorage.h" -template -constexpr std::string_view type_name() -{ - using namespace std; -#ifdef __clang__ - string_view p = __PRETTY_FUNCTION__; - return string_view(p.data() + 34, p.size() - 34 - 1); -#elif defined(__GNUC__) - string_view p = __PRETTY_FUNCTION__; -#if __cplusplus < 201'402 - return string_view(p.data() + 36, p.size() - 36 - 1); -#else - return string_view(p.data() + 49, p.find(';', 49) - 49); -#endif -#elif defined(_MSC_VER) - string_view p = __FUNCSIG__; - return string_view(p.data() + 84, p.size() - 84 - 7); -#endif - return std::string_view {}; -} +#include +#include struct A { @@ -43,69 +25,43 @@ struct A } }; -class IDelegate -{ -public: +using namespace TGEngine::Core; - template - requires std::invocable - auto execute(Func&& func, Args&&... args) - { - return std::invoke(std::forward(func), std::forward(args)...); - } - - virtual ~IDelegate() = default; -}; - -template -class LambdaDelegate: public IDelegate +int f(A) { -public: - - template - explicit LambdaDelegate(TLambda&& lambda, TPayload&&... payload) - : func(std::forward(lambda)), - payload(std::forward(payload)...) - {} - -private: - - Lambda func; - std::tuple payload; -}; - -template -explicit LambdaDelegate(Lambda&& lambda, Payload&&... payload) - -> LambdaDelegate, std::remove_reference_t...>; + return 2; +} -template -struct Test +int g(int n, const A&) { - explicit Test(T t) : t(t) {}; - T t; -}; + return n * n; +} -template -struct Test +struct S { -}; + int foo(int, A&) { return 1; } -std::string foo(int, double&, char&&); + int bar(int, A) const { return -1; } +}; int main() { - A aa; + S s; + const S cs; + + const A ca; + A a; - LambdaDelegate ld1([aa]() { return 1; }); + Delegate delegate; - auto l = [aa]() { return 2; }; - LambdaDelegate ld2(l, 1); + auto l = [](int, A&) { return 1; }; + delegate.bindLambda(l, A()); + std::cout << delegate.execute(5) << "\n"; - std::cout << type_name() << "\n"; - std::cout << type_name() << "\n"; + delegate.bindStatic(&g, a); - Test t; - std::cout << type_name() << "\n"; + delegate.bindMethod(&S::foo, &s, ca); + delegate.bindMethod(&S::bar, &cs, a); return 0; } \ No newline at end of file From bb0e74091a466056a9a0b94fd7d37579451f8d87 Mon Sep 17 00:00:00 2001 From: Yurasov Ilia Date: Sun, 21 Apr 2024 12:14:50 +0300 Subject: [PATCH 11/16] Add alignment for each push in stack storage --- app/include/Core/Memory/StackStorage.h | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/include/Core/Memory/StackStorage.h b/app/include/Core/Memory/StackStorage.h index be223bb..8ea4081 100644 --- a/app/include/Core/Memory/StackStorage.h +++ b/app/include/Core/Memory/StackStorage.h @@ -1,12 +1,11 @@ -#ifndef STACKALLOCATOR_H -#define STACKALLOCATOR_H +#ifndef TGENGINE_STACK_ALLOCATOR_H +#define TGENGINE_STACK_ALLOCATOR_H #include #include #include #include - template class StackStorage { @@ -14,7 +13,7 @@ class StackStorage constexpr StackStorage() : buffer() - , ptr(buffer.data()) + , ptr(buffer) {} StackStorage(const StackStorage&) = delete; @@ -29,39 +28,44 @@ class StackStorage void* push(size_t bytes) { void* currentPtr = ptr; - ptr += bytes; + ptr += alignUp(bytes); return currentPtr; } void pop(const void* p, size_t bytes) { - assert(p != ptr - bytes); + bytes = alignUp(bytes); + assert(p == ptr - bytes); ptr -= bytes; } [[nodiscard]] bool contains(const void* p) const { - return p && p >= buffer.cbegin() && p < buffer.cend(); + return p && p >= buffer && p < buffer + N; } [[nodiscard]] size_t used() const { - return std::distance(ptr, buffer.data()); + return static_cast(ptr - buffer); } [[nodiscard]] bool isEmpty() const { - return ptr == buffer.cbegin(); + return ptr == buffer; } private: - alignas(Align) std::array buffer; + static size_t alignUp(std::size_t n) noexcept { return (n + (align - 1)) & ~(align - 1); } + +private: + + alignas(align) std::byte buffer[size]; std::byte* ptr; }; -#endif // STACKALLOCATOR_H +#endif // TGENGINE_STACK_ALLOCATOR_H From d05ea1ae42567aaf74b65132c0fdc6c7642c83ae Mon Sep 17 00:00:00 2001 From: Yurasov Ilia Date: Sat, 27 Apr 2024 21:38:04 +0300 Subject: [PATCH 12/16] Add check for pushing in the storage --- app/include/Core/Memory/StackStorage.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/include/Core/Memory/StackStorage.h b/app/include/Core/Memory/StackStorage.h index 8ea4081..b0657bd 100644 --- a/app/include/Core/Memory/StackStorage.h +++ b/app/include/Core/Memory/StackStorage.h @@ -1,10 +1,8 @@ -#ifndef TGENGINE_STACK_ALLOCATOR_H -#define TGENGINE_STACK_ALLOCATOR_H +#ifndef TGENGINE_STACK_STORAGE_H +#define TGENGINE_STACK_STORAGE_H -#include #include #include -#include template class StackStorage @@ -27,6 +25,8 @@ class StackStorage [[nodiscard]] void* push(size_t bytes) { + if (!canAllocate(bytes)) return nullptr; + void* currentPtr = ptr; ptr += alignUp(bytes); return currentPtr; @@ -39,6 +39,12 @@ class StackStorage ptr -= bytes; } + [[nodiscard]] + bool canAllocate(size_t bytes) + { + return used() + bytes <= size; + } + [[nodiscard]] bool contains(const void* p) const { @@ -68,4 +74,4 @@ class StackStorage }; -#endif // TGENGINE_STACK_ALLOCATOR_H +#endif // TGENGINE_STACK_STORAGE_H From fa3aaf87649c8de22cdae96f4bbdbb98a556ce24 Mon Sep 17 00:00:00 2001 From: Yurasov Ilia Date: Sat, 27 Apr 2024 21:41:18 +0300 Subject: [PATCH 13/16] Add allocateCopy method to DelegateAllocator --- .../Core/Delegates/DelegateAllocator.h | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/app/include/Core/Delegates/DelegateAllocator.h b/app/include/Core/Delegates/DelegateAllocator.h index 18323fa..5c38ca3 100644 --- a/app/include/Core/Delegates/DelegateAllocator.h +++ b/app/include/Core/Delegates/DelegateAllocator.h @@ -1,5 +1,5 @@ -#ifndef DELEGATEALLOCATOR_H -#define DELEGATEALLOCATOR_H +#ifndef TGENGINE_DELEGATE_ALLOCATOR_H +#define TGENGINE_DELEGATE_ALLOCATOR_H #include "Core/Memory/StackStorage.h" #include "DelegateTypes.h" @@ -7,27 +7,42 @@ namespace TGEngine::Core { -template +// TODO: implement DelegateType concept +template class DelegateAllocator { public: - inline static constexpr size_t size = 32; - inline static constexpr size_t align = 8; - - using Storage = StackStorage; - using value_type = DelegateType; + using ErasedDelegate = DelegateType::Erased; - explicit DelegateAllocator(Storage& storage) + constexpr explicit DelegateAllocator(Storage& storage) : storage(storage) {} - DelegateType* allocate(size_t n) + constexpr DelegateAllocator(const DelegateAllocator& other) + : storage(other.storage) + {} + + template + constexpr explicit DelegateAllocator(const DelegateAllocator& other) + : storage(other.storage) + {} + + template + bool operator== (const DelegateAllocator& allocator) const + { + return std::is_same_v && &storage == &allocator.storage; + } + +public: + + [[nodiscard]] + DelegateType* allocate(size_t) { - const size_t bytes = n * sizeof(DelegateType); - void* ptr = canAllocateInStorage(bytes) ? storage.push(bytes) : std::malloc(bytes); - return static_cast(ptr); + const size_t bytes = sizeof(DelegateType); + auto ptr = canAllocateInStorage(bytes) ? storage.push(bytes) : std::malloc(bytes); + return static_cast(ptr); } void deallocate(DelegateType* delegate, size_t) @@ -36,10 +51,20 @@ class DelegateAllocator hasStorageAllocation(delegate) ? storage.pop(delegate, used) : std::free(delegate); } + [[nodiscard]] + ErasedDelegate* allocateCopy(const Storage& copyStorage) + { + const size_t bytes = copyStorage.used(); + auto ptr = canAllocateInStorage(bytes) ? storage.push(bytes) : std::malloc(bytes); + return static_cast(ptr); + } + +private: + [[nodiscard]] bool canAllocateInStorage(size_t bytes) const { - return storage.used() + bytes <= size && storage.isEmpty(); + return storage.isEmpty() && storage.canAllocate(bytes); } [[nodiscard]] @@ -55,4 +80,4 @@ class DelegateAllocator } // namespace TGEngine::Core -#endif // DELEGATEALLOCATOR_H +#endif // TGENGINE_DELEGATE_ALLOCATOR_H From f844f87aaba5c21ebb9f4514e77833a0056ef5ea Mon Sep 17 00:00:00 2001 From: Yurasov Ilia Date: Sat, 27 Apr 2024 21:45:57 +0300 Subject: [PATCH 14/16] Add constructor for copy Delegate --- app/include/Core/Delegates/Delegate.h | 183 ++++++++++--------- app/include/Core/Delegates/DelegateTypes.h | 48 ++--- app/include/Core/Delegates/ErasedFunctions.h | 30 +++ 3 files changed, 152 insertions(+), 109 deletions(-) create mode 100644 app/include/Core/Delegates/ErasedFunctions.h diff --git a/app/include/Core/Delegates/Delegate.h b/app/include/Core/Delegates/Delegate.h index b8d1be1..e5c7547 100644 --- a/app/include/Core/Delegates/Delegate.h +++ b/app/include/Core/Delegates/Delegate.h @@ -4,79 +4,68 @@ #include "DelegateAllocator.h" #include "DelegateTypes.h" -#include #include -#include namespace TGEngine::Core { -template -concept Invocable = requires(Function func, Args... args) { - { - func(args...) - } -> std::same_as; -}; - -template>> -class Binder; +template +class Delegate; -template -class Binder +template +class Delegate { public: - Binder() + Delegate() : executer(nullptr) - , delegate(nullptr) + , constructor(nullptr) + , erasedDelegate(nullptr) , storage() - , allocator(storage) - { - std::cout << type_name() << "\n"; - } + , defaultAllocator(storage) + {} - Binder(const Binder&) = delete; - Binder& operator= (const Binder&) = delete; - - Binder(Binder&& otherBinder) noexcept - : executer(otherBinder.executer) - , delegate(otherBinder.delegate) + Delegate(const Delegate& delegate) + : executer(delegate.executer) + , constructor(delegate.constructor) + , erasedDelegate(nullptr) + , storage() + , defaultAllocator(storage) { - otherBinder.executer = nullptr; - otherBinder.delegate = nullptr; + erasedDelegate = defaultAllocator.allocateCopy(delegate.storage); + constructor(erasedDelegate, delegate.erasedDelegate); } - Binder& operator= (Binder&& otherBinder) noexcept + Delegate& operator= (const Delegate& delegate) { - Binder binder = std::move(otherBinder); - swap(binder); + if (this == &delegate) return *this; + unbind(); + + executer = delegate.executer; + constructor = delegate.constructor; + erasedDelegate = defaultAllocator.allocateCopy(delegate.storage); + constructor(erasedDelegate, delegate.erasedDelegate); return *this; } - ~Binder() { unbind(); } - -private: - - void swap(Binder& binder) noexcept - { - std::swap(executer, binder.executer); - std::swap(delegate, binder.delegate); - } + ~Delegate() { unbind(); } public: template void bindStatic(Types::FunctionPtr function, std::decay_t... payload) { + using namespace _DelegateInternals; using DelegateType = StaticDelegate; bind(function, std::move(payload)...); } template void bindLambda(Lambda&& lambda, Payload... payload) - requires Invocable + requires std::invocable { + using namespace _DelegateInternals; using DelegateType = LambdaDelegate; bind(std::forward(lambda), std::move(payload)...); } @@ -85,8 +74,8 @@ class Binder void bindMethod(Types::NotConstMethodPtr method, T* object, std::decay_t... payload) { - constexpr bool IsConst = false; - using DelegateType = MethodDelegate; + using namespace _DelegateInternals; + using DelegateType = MethodDelegate; bind(method, object, std::move(payload)...); } @@ -94,26 +83,28 @@ class Binder void bindMethod(Types::ConstMethodPtr method, const T* object, std::decay_t... payload) { - constexpr bool IsConst = true; - using DelegateType = MethodDelegate; + using namespace _DelegateInternals; + using DelegateType = MethodDelegate; bind(method, object, std::move(payload)...); } private: - template - void bind(Callable&& callable, ConstructionArgs&&... args) + template + void bind(ConstructionArgs&&... args) { - using DelegateTypeAllocator = typename std::allocator_traits::template rebind_alloc; + using Allocator = std::allocator_traits::template rebind_alloc; + using AllocTraits = std::allocator_traits; if (isBound()) unbind(); - DelegateTypeAllocator delegateTypeAllocator(storage); - auto specificDelegate = std::allocator_traits::allocate(delegateTypeAllocator, 1); - std::allocator_traits::construct(allocator, specificDelegate, std::forward(callable), - std::forward(args)...); - delegate = specificDelegate; - executer = execute; + Allocator allocator(storage); + DelegateType* delegate = AllocTraits::allocate(allocator, 1); + AllocTraits::construct(allocator, delegate, std::forward(args)...); + + erasedDelegate = delegate; + executer = _DelegateInternals::ErasedFunctions::execute; + constructor = static_cast(_DelegateInternals::ErasedFunctions::construct); } public: @@ -121,63 +112,83 @@ class Binder [[nodiscard]] bool isBound() const { - return delegate != nullptr; + return erasedDelegate != nullptr; } void unbind() { - std::allocator_traits::destroy(allocator, delegate); - std::allocator_traits::deallocate(allocator, delegate, 1); + using AllocTraits = std::allocator_traits; + + AllocTraits::destroy(defaultAllocator, erasedDelegate); + AllocTraits::deallocate(defaultAllocator, erasedDelegate, 1); } -private: +public: - using ErasedDelegate = IDelegate; + [[nodiscard]] + RetVal execute(Args... args) const + { + return executer(erasedDelegate, std::forward(args)...); + } - template - constexpr static RetVal execute(ErasedDelegate* delegate, Args&&... args) + [[nodiscard]] + std::optional executeIfBound(Args... args) const { - assert(delegate); - return static_cast(*delegate).execute(std::forward(args)...); + if (!isBound()) return std::nullopt; + return executer(erasedDelegate, std::forward(args)...); } -protected: +private: + using ErasedDelegate = _DelegateInternals::IDelegate; using Executer = Types::FunctionPtr; + using Constructor = Types::FunctionPtr; - Executer executer; - ErasedDelegate* delegate; + Executer executer; // Try to move to IDelegate + Constructor constructor; + ErasedDelegate* erasedDelegate; - StackStorage<32, 8> storage; - Allocator allocator; -}; + struct ControlBlock; + friend struct ControlBlock; + ControlBlock block; -template>> -class Delegate; +private: -template -class Delegate final: public Binder + static constexpr size_t storageSize = 64; + static constexpr size_t storageAlign = 8; + + using Storage = StackStorage; + using DefaultAllocator = DelegateAllocator; + + Storage storage; // Try to call new for storage + DefaultAllocator defaultAllocator; +}; + +template +struct Delegate::ControlBlock { -public: + ControlBlock() + : executer(nullptr) + , constructor(nullptr) + , erasedDelegate(nullptr) + {} - using Super = Binder; + explicit ControlBlock(DefaultAllocator& allocator, const Delegate& delegate) + : executer(delegate.block.executer) + , constructor(delegate.block.constructor) + , erasedDelegate(constructor(allocator.allocateCopy(delegate.storage)), delegate.erasedDelegate) + {} - [[nodiscard]] - RetVal execute(Args... args) const - { - return Super::executer(Super::delegate, std::forward(args)...); - } + using ErasedDelegate = _DelegateInternals::IDelegate; + using Executer = Types::FunctionPtr; + using Constructor = Types::FunctionPtr; - [[nodiscard]] - std::optional executeIfBound(Args... args) const - { - if (!Super::isBound()) return std::nullopt; - return Super::executer(Super::delegate, std::forward(args)...); - } + Executer executer; // Try to move to IDelegate + Constructor constructor; + ErasedDelegate* erasedDelegate; }; - } // namespace TGEngine::Core diff --git a/app/include/Core/Delegates/DelegateTypes.h b/app/include/Core/Delegates/DelegateTypes.h index d50ce56..7bb3002 100644 --- a/app/include/Core/Delegates/DelegateTypes.h +++ b/app/include/Core/Delegates/DelegateTypes.h @@ -2,6 +2,7 @@ #define TGENGINE_DELEGATE_TYPES_H #include "Core/Types/Callable.h" +#include "ErasedFunctions.h" #include #include @@ -28,17 +29,21 @@ constexpr std::string_view type_name() return std::string_view {}; } -namespace TGEngine::Core +namespace TGEngine::Core::_DelegateInternals { template class IDelegate; -template -class IDelegate +template +class IDelegate { public: + using RetVal = R; + using Signature = RetVal(Args...); + using Erased = IDelegate; + virtual ~IDelegate() = default; }; @@ -54,11 +59,9 @@ class StaticDelegate: public IDelegate(payload)...) - { - std::cout << "\nStaticDelegate\n"; - std::cout << type_namefunction)>() << "\n"; - std::cout << type_namepayload)>() << "\n"; - } + {} + + constexpr StaticDelegate(const StaticDelegate& delegate) = default; [[nodiscard]] constexpr RetVal execute(Args&&... args) @@ -69,7 +72,6 @@ class StaticDelegate: public IDelegate - [[nodiscard]] constexpr RetVal executeWithPayload(Args&&... args, std::index_sequence) { return function(std::forward(args)..., std::get(payload)...); @@ -78,6 +80,8 @@ class StaticDelegate: public IDelegate payload; }; @@ -93,11 +97,9 @@ class LambdaDelegate: public IDelegate(lambda)) , payload(std::forward(payload)...) - { - std::cout << "\nLambdaDelegate\n"; - std::cout << type_namelambda)>() << "\n"; - std::cout << type_namepayload)>() << "\n"; - } + {} + + constexpr LambdaDelegate(const LambdaDelegate& delegate) = default; [[nodiscard]] constexpr RetVal execute(Args&&... args) @@ -108,7 +110,6 @@ class LambdaDelegate: public IDelegate - [[nodiscard]] constexpr RetVal executeWithPayload(Args&&... args, std::index_sequence) { return lambda(std::forward(args)..., std::get(payload)...); @@ -116,7 +117,10 @@ class LambdaDelegate: public IDelegate lambda; + + [[no_unique_address]] std::tuple payload; }; @@ -135,12 +139,9 @@ class MethodDelegate: public ID : method(method) , object(object) , payload(std::forward(payload)...) - { - std::cout << "\nMethodDelegate\n"; - std::cout << type_namemethod)>() << "\n"; - std::cout << type_nameobject)>() << "\n"; - std::cout << type_namepayload)>() << "\n"; - } + {} + + constexpr MethodDelegate(const MethodDelegate& delegate) = default; [[nodiscard]] constexpr RetVal execute(Args&&... args) @@ -151,7 +152,6 @@ class MethodDelegate: public ID private: template - [[nodiscard]] constexpr RetVal executeWithPayload(Args&&... args, std::index_sequence) { return (object->*method)(std::forward(args)..., std::get(payload)...); @@ -161,9 +161,11 @@ class MethodDelegate: public ID Method method; Object object; + + [[no_unique_address]] std::tuple payload; }; -} // namespace TGEngine::Core +} // namespace TGEngine::Core::_DelegateInternals #endif // TGENGINE_DELEGATE_TYPES_H diff --git a/app/include/Core/Delegates/ErasedFunctions.h b/app/include/Core/Delegates/ErasedFunctions.h new file mode 100644 index 0000000..d55d4cf --- /dev/null +++ b/app/include/Core/Delegates/ErasedFunctions.h @@ -0,0 +1,30 @@ +#ifndef TGENGINE_DELEGATE_INTERNALS_H +#define TGENGINE_DELEGATE_INTERNALS_H + +#include + +namespace TGEngine::Core::_DelegateInternals +{ + +struct ErasedFunctions +{ + template + constexpr static DelegateType::RetVal execute(DelegateType::Erased* delegate, Args&&... args) + { + return static_cast(delegate)->execute(std::forward(args)...); + } + + template + constexpr static DelegateType::Erased* construct(DelegateType::Erased* at, const DelegateType::Erased* from) + { + auto p = static_cast(at); + auto arg = static_cast(from); + + return std::construct_at(p, *arg); + } +}; + + +} // namespace TGEngine::Core::_DelegateInternals + +#endif // TGENGINE_DELEGATE_INTERNALS_H From 8d65c3be727c7e656eb7246bd024ff5044bc5068 Mon Sep 17 00:00:00 2001 From: Yurasov Ilia Date: Sat, 27 Apr 2024 21:46:53 +0300 Subject: [PATCH 15/16] Change main --- app/src/main.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/src/main.cpp b/app/src/main.cpp index d3f699a..660bf60 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -1,7 +1,5 @@ #include "Core/Delegates/Delegate.h" -#include "Core/Memory/StackStorage.h" -#include #include struct A @@ -26,8 +24,9 @@ struct A }; using namespace TGEngine::Core; +using namespace TGEngine::Types; -int f(A) +int f(int) { return 2; } @@ -42,6 +41,8 @@ struct S int foo(int, A&) { return 1; } int bar(int, A) const { return -1; } + + int hul(int, double) {return 0;} }; int main() @@ -54,14 +55,19 @@ int main() Delegate delegate; - auto l = [](int, A&) { return 1; }; - delegate.bindLambda(l, A()); - std::cout << delegate.execute(5) << "\n"; + auto l = [](int, double) { return 1; }; + delegate.bindLambda(l ,3.14); + delegate.bindLambda([](int) {return 1;}); - delegate.bindStatic(&g, a); + delegate.bindStatic(&g, A()); + delegate.bindStatic(&f); delegate.bindMethod(&S::foo, &s, ca); + delegate.bindMethod(&S::hul, &s, 1.0); delegate.bindMethod(&S::bar, &cs, a); + Delegate delegate1 = delegate; + std::cout << delegate1.execute(1) << " \n"; + return 0; } \ No newline at end of file From a4a40e8b58cd10b7c3f0ecb09dc55976572f6669 Mon Sep 17 00:00:00 2001 From: Yurasov Ilia Date: Sun, 5 Jan 2025 23:32:02 +0300 Subject: [PATCH 16/16] save changes --- app/include/Core/Delegates/Delegate.h | 196 ++++++------- .../Core/Delegates/DelegateAllocator.h | 60 ++-- app/include/Core/Delegates/DelegateTypes.h | 36 +-- app/include/Core/Delegates/ErasedFunctions.h | 30 +- app/include/Core/Memory/StackStorage.h | 20 +- app/include/Core/Types/Callable.h | 4 +- app/src/main.cpp | 24 +- .../api/v1/query/client-vscode/query.json | 1 + build/CMakeCache.txt | 65 +++++ build/CMakeFiles/cmake.check_cache | 1 + docs/Doxyfile | 10 +- tests/CMakeLists.txt | 2 +- .../DelegateAllocator/AlocationDeffender.h | 40 +++ .../DelegateAllocatorTest.cpp | 218 ++++++++++++++ tests/Delegates/DelegateTest.cpp | 6 + tests/Memory/StackStorageTest.cpp | 276 ++++++++++++++++++ tests/TestExample.cpp | 6 - 17 files changed, 787 insertions(+), 208 deletions(-) create mode 100644 build/.cmake/api/v1/query/client-vscode/query.json create mode 100644 build/CMakeCache.txt create mode 100644 build/CMakeFiles/cmake.check_cache create mode 100644 tests/Delegates/DelegateAllocator/AlocationDeffender.h create mode 100644 tests/Delegates/DelegateAllocator/DelegateAllocatorTest.cpp create mode 100644 tests/Delegates/DelegateTest.cpp create mode 100644 tests/Memory/StackStorageTest.cpp delete mode 100644 tests/TestExample.cpp diff --git a/app/include/Core/Delegates/Delegate.h b/app/include/Core/Delegates/Delegate.h index e5c7547..692e97a 100644 --- a/app/include/Core/Delegates/Delegate.h +++ b/app/include/Core/Delegates/Delegate.h @@ -1,40 +1,45 @@ -#ifndef DELEGATE_H -#define DELEGATE_H +#ifndef TGENGINE_DELEGATE_H +#define TGENGINE_DELEGATE_H +#include "Core/Memory/StackStorage.h" +#include "Core/Types/Callable.h" #include "DelegateAllocator.h" -#include "DelegateTypes.h" +#include "ErasedFunctions.h" #include namespace TGEngine::Core { + template class Delegate; template class Delegate { +private: + + using ErasedDelegate = _DelegateInternals::IDelegate; + + using Executer = FunctionPtr; + using Constructor = FunctionPtr; + + static constexpr size_t storageSize = 64; + static constexpr size_t storageAlign = 8; + using Storage = StackStorage; + using DefaultAllocator = _DelegateInternals::DelegateAllocator; + public: - Delegate() - : executer(nullptr) - , constructor(nullptr) - , erasedDelegate(nullptr) - , storage() - , defaultAllocator(storage) - {} + Delegate() = default; Delegate(const Delegate& delegate) - : executer(delegate.executer) + : defaultAllocator(storage) + , executer(delegate.executer) , constructor(delegate.constructor) - , erasedDelegate(nullptr) - , storage() - , defaultAllocator(storage) - { - erasedDelegate = defaultAllocator.allocateCopy(delegate.storage); - constructor(erasedDelegate, delegate.erasedDelegate); - } + , erasedDelegate(makeCopy(delegate)) + {} Delegate& operator= (const Delegate& delegate) { @@ -43,68 +48,19 @@ class Delegate executer = delegate.executer; constructor = delegate.constructor; - erasedDelegate = defaultAllocator.allocateCopy(delegate.storage); - constructor(erasedDelegate, delegate.erasedDelegate); + erasedDelegate = makeCopy(delegate); return *this; } ~Delegate() { unbind(); } -public: - - template - void bindStatic(Types::FunctionPtr function, std::decay_t... payload) - { - using namespace _DelegateInternals; - using DelegateType = StaticDelegate; - bind(function, std::move(payload)...); - } - - template - void bindLambda(Lambda&& lambda, Payload... payload) - requires std::invocable - { - using namespace _DelegateInternals; - using DelegateType = LambdaDelegate; - bind(std::forward(lambda), std::move(payload)...); - } - - template - void bindMethod(Types::NotConstMethodPtr method, T* object, - std::decay_t... payload) - { - using namespace _DelegateInternals; - using DelegateType = MethodDelegate; - bind(method, object, std::move(payload)...); - } - - template - void bindMethod(Types::ConstMethodPtr method, const T* object, - std::decay_t... payload) - { - using namespace _DelegateInternals; - using DelegateType = MethodDelegate; - bind(method, object, std::move(payload)...); - } - private: - template - void bind(ConstructionArgs&&... args) + ErasedDelegate* makeCopy(const Delegate& delegate) { - using Allocator = std::allocator_traits::template rebind_alloc; - using AllocTraits = std::allocator_traits; - - if (isBound()) unbind(); - - Allocator allocator(storage); - DelegateType* delegate = AllocTraits::allocate(allocator, 1); - AllocTraits::construct(allocator, delegate, std::forward(args)...); - - erasedDelegate = delegate; - executer = _DelegateInternals::ErasedFunctions::execute; - constructor = static_cast(_DelegateInternals::ErasedFunctions::construct); + ErasedDelegate* copy = defaultAllocator.allocateCopy(delegate.storage); + return constructor(copy, delegate.erasedDelegate); } public: @@ -138,58 +94,82 @@ class Delegate return executer(erasedDelegate, std::forward(args)...); } -private: +public: - using ErasedDelegate = _DelegateInternals::IDelegate; - using Executer = Types::FunctionPtr; - using Constructor = Types::FunctionPtr; + template + void bindStatic(FunctionPtr function, std::decay_t... payload) + { + using namespace _DelegateInternals; + using DelegateType = StaticDelegate; + bind(function, std::move(payload)...); + } - Executer executer; // Try to move to IDelegate - Constructor constructor; - ErasedDelegate* erasedDelegate; + template + void bindLambda(Lambda&& lambda, Payload... payload) + requires std::invocable + { + using namespace _DelegateInternals; + using DelegateType = LambdaDelegate; + bind(std::forward(lambda), std::move(payload)...); + } - struct ControlBlock; - friend struct ControlBlock; + template + void bindMethod(NotConstMethodPtr method, T* object, + std::decay_t... payload) + { + constexpr bool isConst = false; + using namespace _DelegateInternals; + using DelegateType = MethodDelegate; + bind(method, object, std::move(payload)...); + } - ControlBlock block; + template + void bindMethod(ConstMethodPtr method, const T* object, + std::decay_t... payload) + { + constexpr bool isConst = true; + using namespace _DelegateInternals; + using DelegateType = MethodDelegate; + bind(method, object, std::move(payload)...); + } private: - static constexpr size_t storageSize = 64; - static constexpr size_t storageAlign = 8; + template<_DelegateInternals::DelegateType DelegateType, typename... ConstructionArgs> + void bind(ConstructionArgs&&... args) + { + if (isBound()) unbind(); - using Storage = StackStorage; - using DefaultAllocator = DelegateAllocator; + erasedDelegate = makeDelegate(std::forward(args)...); + executer = _DelegateInternals::execute; + constructor = _DelegateInternals::construct; + } - Storage storage; // Try to call new for storage - DefaultAllocator defaultAllocator; -}; + template + ErasedDelegate* makeDelegate(ConstructionArgs&&... args) + { + using Allocator = typename std::allocator_traits::template rebind_alloc; + using AllocTraits = std::allocator_traits; -template -struct Delegate::ControlBlock -{ - ControlBlock() - : executer(nullptr) - , constructor(nullptr) - , erasedDelegate(nullptr) - {} + Allocator allocator(storage); + DelegateType* delegate = AllocTraits::allocate(allocator, 1); + AllocTraits::construct(allocator, delegate, std::forward(args)...); - explicit ControlBlock(DefaultAllocator& allocator, const Delegate& delegate) - : executer(delegate.block.executer) - , constructor(delegate.block.constructor) - , erasedDelegate(constructor(allocator.allocateCopy(delegate.storage)), delegate.erasedDelegate) - {} + return delegate; + } - using ErasedDelegate = _DelegateInternals::IDelegate; - using Executer = Types::FunctionPtr; - using Constructor = Types::FunctionPtr; +private: + + Storage storage; + DefaultAllocator defaultAllocator {storage}; - Executer executer; // Try to move to IDelegate - Constructor constructor; - ErasedDelegate* erasedDelegate; + Executer executer {}; + Constructor constructor {}; + ErasedDelegate* erasedDelegate {}; }; + } // namespace TGEngine::Core -#endif // DELEGATE_H +#endif // TGENGINE_DELEGATE_H diff --git a/app/include/Core/Delegates/DelegateAllocator.h b/app/include/Core/Delegates/DelegateAllocator.h index 5c38ca3..3e5e9ed 100644 --- a/app/include/Core/Delegates/DelegateAllocator.h +++ b/app/include/Core/Delegates/DelegateAllocator.h @@ -1,20 +1,18 @@ #ifndef TGENGINE_DELEGATE_ALLOCATOR_H #define TGENGINE_DELEGATE_ALLOCATOR_H -#include "Core/Memory/StackStorage.h" #include "DelegateTypes.h" -namespace TGEngine::Core +namespace TGEngine::Core::_DelegateInternals { -// TODO: implement DelegateType concept -template +template class DelegateAllocator { public: - using value_type = DelegateType; - using ErasedDelegate = DelegateType::Erased; + using value_type = Delegate; + using ErasedDelegate = Delegate::Erased; constexpr explicit DelegateAllocator(Storage& storage) : storage(storage) @@ -24,60 +22,58 @@ class DelegateAllocator : storage(other.storage) {} - template - constexpr explicit DelegateAllocator(const DelegateAllocator& other) + template + constexpr explicit DelegateAllocator(const DelegateAllocator& other) : storage(other.storage) {} - template - bool operator== (const DelegateAllocator& allocator) const + template + friend class DelegateAllocator; + + template + bool operator== (const DelegateAllocator& allocator) const { - return std::is_same_v && &storage == &allocator.storage; + return &storage == &allocator.storage && std::is_base_of_v; } public: [[nodiscard]] - DelegateType* allocate(size_t) + Delegate* allocate(size_t) { - const size_t bytes = sizeof(DelegateType); - auto ptr = canAllocateInStorage(bytes) ? storage.push(bytes) : std::malloc(bytes); - return static_cast(ptr); - } - - void deallocate(DelegateType* delegate, size_t) - { - const size_t used = storage.used(); - hasStorageAllocation(delegate) ? storage.pop(delegate, used) : std::free(delegate); + const size_t bytes = sizeof(Delegate); + return static_cast(reserve(bytes)); } [[nodiscard]] ErasedDelegate* allocateCopy(const Storage& copyStorage) { const size_t bytes = copyStorage.used(); - auto ptr = canAllocateInStorage(bytes) ? storage.push(bytes) : std::malloc(bytes); - return static_cast(ptr); + return static_cast(reserve(bytes)); } -private: - - [[nodiscard]] - bool canAllocateInStorage(size_t bytes) const + void deallocate(Delegate* delegate, size_t) { - return storage.isEmpty() && storage.canAllocate(bytes); + const size_t used = storage.used(); + hasStorageAllocation(delegate) ? storage.deallocate(delegate, used) : std::free(delegate); } - [[nodiscard]] - bool hasStorageAllocation(DelegateType* delegate) const +private: + + void* reserve(std::size_t bytes) { - return storage.contains(delegate); + 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 +} // namespace TGEngine::Core::_DelegateInternals #endif // TGENGINE_DELEGATE_ALLOCATOR_H diff --git a/app/include/Core/Delegates/DelegateTypes.h b/app/include/Core/Delegates/DelegateTypes.h index 7bb3002..523f774 100644 --- a/app/include/Core/Delegates/DelegateTypes.h +++ b/app/include/Core/Delegates/DelegateTypes.h @@ -1,34 +1,9 @@ #ifndef TGENGINE_DELEGATE_TYPES_H #define TGENGINE_DELEGATE_TYPES_H -#include "Core/Types/Callable.h" -#include "ErasedFunctions.h" - #include -#include #include -template -constexpr std::string_view type_name() -{ - using namespace std; -#ifdef __clang__ - string_view p = __PRETTY_FUNCTION__; - return string_view(p.data() + 34, p.size() - 34 - 1); -#elif defined(__GNUC__) - string_view p = __PRETTY_FUNCTION__; -#if __cplusplus < 201'402 - return string_view(p.data() + 36, p.size() - 36 - 1); -#else - return string_view(p.data() + 49, p.find(';', 49) - 49); -#endif -#elif defined(_MSC_VER) - string_view p = __FUNCSIG__; - return string_view(p.data() + 84, p.size() - 84 - 7); -#endif - return std::string_view {}; -} - namespace TGEngine::Core::_DelegateInternals { @@ -42,11 +17,20 @@ class IDelegate using RetVal = R; using Signature = RetVal(Args...); - using Erased = IDelegate; + using Erased = IDelegate; virtual ~IDelegate() = default; }; +template +concept DelegateType = requires { + typename T::RetVal; + typename T::Signature; + typename T::Erased; + + static_cast>(std::declval()); +} && std::same_as>; + template class StaticDelegate; diff --git a/app/include/Core/Delegates/ErasedFunctions.h b/app/include/Core/Delegates/ErasedFunctions.h index d55d4cf..6fc8e63 100644 --- a/app/include/Core/Delegates/ErasedFunctions.h +++ b/app/include/Core/Delegates/ErasedFunctions.h @@ -1,29 +1,27 @@ #ifndef TGENGINE_DELEGATE_INTERNALS_H #define TGENGINE_DELEGATE_INTERNALS_H +#include "DelegateTypes.h" + #include namespace TGEngine::Core::_DelegateInternals { -struct ErasedFunctions +template +constexpr DelegateType::RetVal execute(typename DelegateType::Erased* delegate, Args&&... args) +{ + return static_cast(delegate)->execute(std::forward(args)...); +} + +template +constexpr DelegateType::Erased* construct(typename DelegateType::Erased* at, const typename DelegateType::Erased* from) { - template - constexpr static DelegateType::RetVal execute(DelegateType::Erased* delegate, Args&&... args) - { - return static_cast(delegate)->execute(std::forward(args)...); - } - - template - constexpr static DelegateType::Erased* construct(DelegateType::Erased* at, const DelegateType::Erased* from) - { - auto p = static_cast(at); - auto arg = static_cast(from); - - return std::construct_at(p, *arg); - } -}; + auto p = static_cast(at); + auto arg = static_cast(from); + return std::construct_at(p, *arg); +} } // namespace TGEngine::Core::_DelegateInternals diff --git a/app/include/Core/Memory/StackStorage.h b/app/include/Core/Memory/StackStorage.h index b0657bd..82be381 100644 --- a/app/include/Core/Memory/StackStorage.h +++ b/app/include/Core/Memory/StackStorage.h @@ -4,6 +4,9 @@ #include #include +namespace TGEngine::Core +{ + template class StackStorage { @@ -23,7 +26,7 @@ class StackStorage public: [[nodiscard]] - void* push(size_t bytes) + constexpr void* allocate(size_t bytes) { if (!canAllocate(bytes)) return nullptr; @@ -32,7 +35,7 @@ class StackStorage return currentPtr; } - void pop(const void* p, size_t bytes) + constexpr void deallocate([[maybe_unused]] const void* p, size_t bytes) { bytes = alignUp(bytes); assert(p == ptr - bytes); @@ -40,32 +43,32 @@ class StackStorage } [[nodiscard]] - bool canAllocate(size_t bytes) + constexpr bool canAllocate(size_t bytes) const { - return used() + bytes <= size; + return bytes > 0 && used() + bytes <= size; } [[nodiscard]] - bool contains(const void* p) const + constexpr bool contains(const void* p) const { return p && p >= buffer && p < buffer + N; } [[nodiscard]] - size_t used() const + constexpr size_t used() const { return static_cast(ptr - buffer); } [[nodiscard]] - bool isEmpty() const + constexpr bool isEmpty() const { return ptr == buffer; } private: - static size_t alignUp(std::size_t n) noexcept { return (n + (align - 1)) & ~(align - 1); } + constexpr static size_t alignUp(std::size_t n) noexcept { return (n + (align - 1)) & ~(align - 1); } private: @@ -73,5 +76,6 @@ class StackStorage std::byte* ptr; }; +} // namespace TGEngine::Core #endif // TGENGINE_STACK_STORAGE_H diff --git a/app/include/Core/Types/Callable.h b/app/include/Core/Types/Callable.h index 3f8ba97..3899a0c 100644 --- a/app/include/Core/Types/Callable.h +++ b/app/include/Core/Types/Callable.h @@ -1,7 +1,7 @@ #ifndef TGENGINE_CALLABLE_H #define TGENGINE_CALLABLE_H -namespace TGEngine::Types +namespace TGEngine::Core { namespace internal @@ -33,7 +33,7 @@ using ConstMethodPtr = RetVal (T::*)(Args...) const; template using FunctionPtr = RetVal (*)(Args...); -} // namespace TGEngine::Types +} // namespace TGEngine::Core #endif // TGENGINE_CALLABLE_H diff --git a/app/src/main.cpp b/app/src/main.cpp index 660bf60..2c315f0 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -24,7 +24,7 @@ struct A }; using namespace TGEngine::Core; -using namespace TGEngine::Types; +using namespace TGEngine::Core; int f(int) { @@ -42,7 +42,20 @@ struct S int bar(int, A) const { return -1; } - int hul(int, double) {return 0;} + int hul(int, double) { return 0; } +}; + +template +struct BB +{ + bool b = B; +}; + +template +void showT(T&) +{ + constexpr bool isConst = std::is_const_v; + std::cout << BB {}.b; }; int main() @@ -56,8 +69,8 @@ int main() Delegate delegate; auto l = [](int, double) { return 1; }; - delegate.bindLambda(l ,3.14); - delegate.bindLambda([](int) {return 1;}); + delegate.bindLambda(l, 3.14); + delegate.bindLambda([](int) { return 1; }); delegate.bindStatic(&g, A()); delegate.bindStatic(&f); @@ -69,5 +82,8 @@ int main() Delegate delegate1 = delegate; std::cout << delegate1.execute(1) << " \n"; + const int r {}; + showT(r); + return 0; } \ No newline at end of file diff --git a/build/.cmake/api/v1/query/client-vscode/query.json b/build/.cmake/api/v1/query/client-vscode/query.json new file mode 100644 index 0000000..82bb964 --- /dev/null +++ b/build/.cmake/api/v1/query/client-vscode/query.json @@ -0,0 +1 @@ +{"requests":[{"kind":"cache","version":2},{"kind":"codemodel","version":2},{"kind":"toolchains","version":1},{"kind":"cmakeFiles","version":1}]} \ No newline at end of file diff --git a/build/CMakeCache.txt b/build/CMakeCache.txt new file mode 100644 index 0000000..6a6bf4a --- /dev/null +++ b/build/CMakeCache.txt @@ -0,0 +1,65 @@ +# This is the CMakeCache file. +# For build in directory: /home/ilia/programming/tg-engine/build +# It was generated by CMake: /usr/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//No help, variable specified on the command line. +CMAKE_BUILD_TYPE:STRING=Debug + +//No help, variable specified on the command line. +CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++ + +//No help, variable specified on the command line. +CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc + +//No help, variable specified on the command line. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE + + +######################## +# INTERNAL cache entries +######################## + +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/home/ilia/programming/tg-engine/build +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=22 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/usr/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Unix Makefiles +//Generator instance identifier. +CMAKE_GENERATOR_INSTANCE:INTERNAL= +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/home/ilia/programming/tg-engine +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.22 + diff --git a/build/CMakeFiles/cmake.check_cache b/build/CMakeFiles/cmake.check_cache new file mode 100644 index 0000000..3dccd73 --- /dev/null +++ b/build/CMakeFiles/cmake.check_cache @@ -0,0 +1 @@ +# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/docs/Doxyfile b/docs/Doxyfile index 5021e14..3b2f98c 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -216,9 +216,9 @@ JAVADOC_BANNER = YES QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# multi-line C++ special comment controlBlock (i.e. a controlBlock of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this +# to treat a multi-line C++ comment controlBlock as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are @@ -568,7 +568,7 @@ HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. +# blocks will be appended to the function's detailed documentation controlBlock. # The default value is: NO. HIDE_IN_BODY_DOCS = NO @@ -1245,7 +1245,7 @@ HTML_FILE_EXTENSION = .html # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. +# of the possible markers and controlBlock names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = @@ -2595,7 +2595,7 @@ PLANTUML_JAR_PATH = PLANTUML_CFG_FILE = # When using plantuml, the specified paths are searched for files specified by -# the !include statement in a plantuml block. +# the !include statement in a plantuml controlBlock. PLANTUML_INCLUDE_PATH = diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 210633e..6201f65 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,5 +17,5 @@ gtest_discover_tests(${TEST_NAME}) add_test(NAME ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - COMMAND ${TEST_NAME} + COMMAND ${TEST_NAME} "--gtest_color=yes" ) \ No newline at end of file diff --git a/tests/Delegates/DelegateAllocator/AlocationDeffender.h b/tests/Delegates/DelegateAllocator/AlocationDeffender.h new file mode 100644 index 0000000..99a755a --- /dev/null +++ b/tests/Delegates/DelegateAllocator/AlocationDeffender.h @@ -0,0 +1,40 @@ +#ifndef TGENGINE_ALOCATION_DEFFENDER_H +#define TGENGINE_ALOCATION_DEFFENDER_H + +#include + +template +class AllocationDeffender +{ +public: + + using DelegateType = typename Allocator::value_type; + + explicit AllocationDeffender(Allocator& allocator) + : allocator(allocator) + , allocations() + {} + + const DelegateType* allocate(std::size_t n) + { + DelegateType* delegate = allocator.allocate(n); + allocations.push_back({delegate, n}); + return delegate; + } + + ~AllocationDeffender() + { + for (auto iter = allocations.rbegin(); iter != allocations.rend(); ++iter) + { + auto& [delegate, n] = *iter; + allocator.deallocate(delegate, n); + } + } + +private: + + Allocator& allocator; + std::vector> allocations; +}; + +#endif //TGENGINE_ALOCATION_DEFFENDER_H diff --git a/tests/Delegates/DelegateAllocator/DelegateAllocatorTest.cpp b/tests/Delegates/DelegateAllocator/DelegateAllocatorTest.cpp new file mode 100644 index 0000000..41a16e1 --- /dev/null +++ b/tests/Delegates/DelegateAllocator/DelegateAllocatorTest.cpp @@ -0,0 +1,218 @@ +#include "Core/Delegates/DelegateAllocator.h" + +#include "AlocationDeffender.h" +#include "Core/Memory/StackStorage.h" +#include "Core/Types/Callable.h" + +#include + +using namespace TGEngine::Core::_DelegateInternals; +using namespace TGEngine::Core; + +using Storage = StackStorage<64, 8>; +using SmallStorage = StackStorage<8, 8>; + +TEST(DelegateAllocatorTest, CopyIsEqualToOriginalAllocator) +{ + Storage storage; + DelegateAllocator, Storage> allocator1(storage); + DelegateAllocator allocator2 = allocator1; + + EXPECT_EQ(allocator1, allocator2); +} + +TEST(DelegateAllocatorTest, AnyAllocatorsWithDifferentDelegateTypeSignatureArentEquals) +{ + Storage storage; + DelegateAllocator, Storage> allocator1(storage); + DelegateAllocator, Storage> allocator2(storage); + + EXPECT_NE(allocator1, allocator2); +} + +TEST(DelegateAllocatorTest, AnyAllocatorsWithTheDifferentStoragesArentEquals) +{ + Storage storage1; + DelegateAllocator, Storage> allocator1(storage1); + + Storage storage2; + DelegateAllocator, Storage> allocator2(storage2); + + EXPECT_NE(allocator1, allocator2); +} + +TEST(DelegateAllocatorTest, AllocatorsWithSameStorageAndCommonBaseDelegateTypeAreEquals) +{ + Storage storage; + DelegateAllocator, Storage> allocator1(storage); + DelegateAllocator, void()>, Storage> allocator2(storage); + DelegateAllocator, void(), double, int>, Storage> allocator3(storage); + + EXPECT_EQ(allocator1, allocator2); + EXPECT_EQ(allocator2, allocator3); +} + +TEST(DelegateAllocatorTest, AlocatesInStorageIfEnoughFreeSpace) +{ + using DelegateType = StaticDelegate, void()>; + + Storage storage; + DelegateAllocator allocator(storage); + AllocationDeffender allocDeffender(allocator); + + const DelegateType* delegate = allocDeffender.allocate(1); + + EXPECT_NE(delegate, nullptr); + EXPECT_TRUE(sizeof(DelegateType) <= storage.size); + EXPECT_TRUE(storage.contains(delegate)); +} + +TEST(DelegateAllocatorTest, AlocatesInHeapIfNotEnoughFreeSpace) +{ + using DelegateType = StaticDelegate, void(), double>; + + SmallStorage storage; + DelegateAllocator allocator(storage); + AllocationDeffender allocDeffender(allocator); + + const DelegateType* delegate = allocDeffender.allocate(1); + + EXPECT_NE(delegate, nullptr); + EXPECT_TRUE(sizeof(DelegateType) > storage.size); + EXPECT_FALSE(storage.contains(delegate)); +} + +TEST(DelegateAllocatorTest, AllocatesInStorageOnlyOneObject) +{ + using DelegateType = StaticDelegate, void()>; + + Storage storage; + DelegateAllocator allocator(storage); + AllocationDeffender allocDeffender(allocator); + + const DelegateType* delegate1 = allocDeffender.allocate(1); + const DelegateType* delegate2 = allocDeffender.allocate(1); + + EXPECT_NE(delegate1, nullptr); + EXPECT_NE(delegate2, nullptr); + EXPECT_TRUE(2 * sizeof(DelegateType) <= storage.size); + EXPECT_TRUE(storage.contains(delegate1)); + EXPECT_FALSE(storage.contains(delegate2)); +} + +TEST(DelegateAllocatorTest, AllocateReservesSizeofValueTypeBytesWhenNIsLarge) +{ + using DelegateType = StaticDelegate, void()>; + + Storage storage; + DelegateAllocator allocator(storage); + AllocationDeffender allocDeffender(allocator); + + const DelegateType* delegate = allocDeffender.allocate(100); + + EXPECT_NE(delegate, nullptr); + EXPECT_EQ(storage.used(), sizeof(DelegateType)); +} + +TEST(DelegateAllocatorTest, AllocateReservesSizeofValueTypeBytesWhenNIsSmall) +{ + using DelegateType = StaticDelegate, void()>; + + Storage storage; + DelegateAllocator allocator(storage); + AllocationDeffender allocDeffender(allocator); + + const DelegateType* delegate = allocDeffender.allocate(0); + + EXPECT_NE(delegate, nullptr); + EXPECT_EQ(storage.used(), sizeof(DelegateType)); +} + +TEST(DelegateAllocatorTest, AfterDeallocationStorageIsEmpty) +{ + Storage storage; + using DelegateType = StaticDelegate, void()>; + DelegateAllocator allocator(storage); + + DelegateType* delegate = allocator.allocate(1); + const size_t usedAfterAlloc = storage.used(); + + allocator.deallocate(delegate, 1); + const size_t usedAfterDealloc = storage.used(); + + EXPECT_NE(usedAfterAlloc, 0); + EXPECT_EQ(usedAfterDealloc, 0); +} + +TEST(DelegateAllocatorTest, DeallocateErasesWholeStorageWhenNIsLarge) +{ + Storage storage; + using DelegateType = StaticDelegate, void()>; + DelegateAllocator allocator(storage); + + DelegateType* delegate = allocator.allocate(1); + const size_t usedAfterAlloc = storage.used(); + + allocator.deallocate(delegate, 100); + const size_t usedAfterDealloc = storage.used(); + + EXPECT_NE(usedAfterAlloc, 0); + EXPECT_EQ(usedAfterDealloc, 0); +} + +TEST(DelegateAllocatorTest, DeallocateErasesWholeStorageWhenNIsSmall) +{ + Storage storage; + using DelegateType = StaticDelegate, void()>; + DelegateAllocator allocator(storage); + + DelegateType* delegate = allocator.allocate(1); + const size_t usedAfterAlloc = storage.used(); + + allocator.deallocate(delegate, 0); + const size_t usedAfterDealloc = storage.used(); + + EXPECT_NE(usedAfterAlloc, 0); + EXPECT_EQ(usedAfterDealloc, 0); +} + +TEST(DelegateAllocatorTest, AllocateCopyReservesTheSameAmountOfMemoryAsAllocatedInOtherStorage) +{ + Storage storage; + using DelegateType = StaticDelegate, void()>; + DelegateAllocator allocator(storage); + + Storage copyStorage; + DelegateAllocator erasedAllocator(copyStorage); + + DelegateType* srcP = allocator.allocate(1); + DelegateType::Erased* dstP = erasedAllocator.allocateCopy(storage); + + EXPECT_NE(srcP, nullptr); + EXPECT_NE(dstP, nullptr); + EXPECT_EQ(storage.used(), copyStorage.used()); + + allocator.deallocate(srcP, 1); + erasedAllocator.deallocate(dstP, 1); +} + +TEST(DelegateAllocatorTest, CantAllocateCopyInTheSameStorage) +{ + Storage storage; + using DelegateType = StaticDelegate, void()>; + DelegateAllocator allocator(storage); + DelegateAllocator erasedAllocator(storage); + + DelegateType* srcP = allocator.allocate(1); + const size_t used = storage.used(); + + DelegateType::Erased* dstP = erasedAllocator.allocateCopy(storage); + + EXPECT_NE(srcP, nullptr); + EXPECT_NE(dstP, nullptr); + EXPECT_EQ(storage.used(), used); + EXPECT_FALSE(storage.contains(dstP)); + + allocator.deallocate(srcP, 1); + erasedAllocator.deallocate(dstP, 1); +} \ No newline at end of file diff --git a/tests/Delegates/DelegateTest.cpp b/tests/Delegates/DelegateTest.cpp new file mode 100644 index 0000000..ed3c19b --- /dev/null +++ b/tests/Delegates/DelegateTest.cpp @@ -0,0 +1,6 @@ +#include + +TEST(DelegateTest, SomeName) +{ + ASSERT_EQ(1, 1); +} \ No newline at end of file diff --git a/tests/Memory/StackStorageTest.cpp b/tests/Memory/StackStorageTest.cpp new file mode 100644 index 0000000..efeb570 --- /dev/null +++ b/tests/Memory/StackStorageTest.cpp @@ -0,0 +1,276 @@ +#include "Core/Memory/StackStorage.h" + +#include +#include +#include + +using namespace TGEngine::Core; + +TEST(StackStorageTest, StorageIsEmptyByDefault) +{ + constexpr size_t size = 64; + const StackStorage storage; + + const bool isEmpty = storage.isEmpty(); + + EXPECT_TRUE(isEmpty); +} + +TEST(StackStorageTest, CantAllocateMoreThanSize) +{ + constexpr size_t size = 64; + const StackStorage storage; + + const bool canAllocate = storage.canAllocate(size + 1); + + EXPECT_FALSE(canAllocate); +} + +TEST(StackStorageTest, CantAllocateZeroBytes) +{ + constexpr size_t size = 64; + const StackStorage storage; + + constexpr bool canAllocate = storage.canAllocate(0); + + EXPECT_FALSE(canAllocate); +} + +TEST(StackStorageTest, CanAllocateBetweenZeroAndSizeBytes) +{ + constexpr size_t size = 64; + StackStorage storage; + + bool canAllocateForAllSizes = true; + for (size_t bytes = 1; bytes < size; ++bytes) + { + canAllocateForAllSizes &= storage.canAllocate(bytes); + } + + EXPECT_TRUE(canAllocateForAllSizes); +} + +TEST(StackStorageTest, PushReturnsNullptrIfCantAllocate) +{ + constexpr size_t size = 64; + StackStorage storage; + + void* zeroBytesBlock = storage.allocate(0); + void* largeBlock = storage.allocate(size + 1); + + EXPECT_EQ(zeroBytesBlock, nullptr); + EXPECT_EQ(largeBlock, nullptr); +} + +TEST(StackStorageTest, StorageIsntEmptyAfterPush) +{ + constexpr size_t size = 64; + StackStorage storage; + + std::ignore = storage.allocate(size); + const bool isEmpty = storage.isEmpty(); + + EXPECT_FALSE(isEmpty); +} + +TEST(StackStorageTest, StorageIsEmptyWhenUsedEqualsZero) +{ + constexpr size_t size = 64; + StackStorage storage; + + const bool isEmpty = storage.isEmpty(); + const size_t used = storage.used(); + + EXPECT_TRUE(isEmpty); + EXPECT_EQ(used, 0); +} + +TEST(StackStorageTest, StorageIsntEmptyWhenUsedDoesntEqualZero) +{ + constexpr size_t size = 64; + StackStorage storage; + + std::ignore = storage.allocate(1); + const bool isEmpty = storage.isEmpty(); + const size_t used = storage.used(); + + EXPECT_FALSE(isEmpty); + EXPECT_NE(used, 0); +} + +TEST(StackStorageTest, StorageAlignsData) +{ + constexpr size_t size = 64; + constexpr size_t align = 8; + StackStorage storage; + + std::array addresses {}; + addresses[0] = reinterpret_cast(storage.allocate(align)); + addresses[1] = reinterpret_cast(storage.allocate(1)); + addresses[2] = reinterpret_cast(storage.allocate(2 * align - 5)); + + for (auto address: addresses) + { + EXPECT_TRUE(address % align == 0); + } + EXPECT_EQ(storage.used(), 32); +} + +TEST(StackStorageTest, AligmentEqualsMaxAlignByDefault) +{ + constexpr size_t size = 64; + StackStorage storage; + + const auto adress = reinterpret_cast(storage.allocate(1)); + + EXPECT_TRUE(adress % alignof(std::max_align_t) == 0); +} + +TEST(StackStorageTest, CanPushWhileHasFreeSpace) +{ + constexpr size_t size = 64; + constexpr size_t align = 8; + StackStorage storage; + + constexpr size_t n = size / align + 1; + std::array addresses {}; + std::ranges::generate(addresses, [&storage] { return storage.allocate(1); }); + + for (auto iter = addresses.begin(); iter < std::prev(addresses.end()); ++iter) + { + EXPECT_NE(*iter, nullptr); + } + EXPECT_EQ(addresses.back(), nullptr); +} + +TEST(StackStorageTest, ConstainsReturnsTrueForPointersFromStorage) +{ + constexpr size_t size = 64; + StackStorage storage; + + const void* p1 = storage.allocate(16); + const void* p2 = storage.allocate(32); + + const bool p1InStorage = storage.contains(p1); + const bool p2InStorage = storage.contains(p2); + + EXPECT_TRUE(p1InStorage); + EXPECT_TRUE(p2InStorage); +} + +TEST(StackStorageTest, ContainsReturnsFalseForNullptr) +{ + constexpr size_t size = 64; + StackStorage storage; + + void* p = storage.allocate(size + 1); + const bool inStorage = storage.contains(p); + + EXPECT_EQ(p, nullptr); + EXPECT_FALSE(inStorage); +} + +TEST(StackStorageTest, ContainsReturnsFalseForPointersOutsideStorage) +{ + constexpr size_t size = 64; + const StackStorage storage; + + int a = 0; + const bool aInStorage = storage.contains(&a); + + const void* p = new int(0); + const bool pInStorage = storage.contains(p); + + EXPECT_FALSE(aInStorage); + EXPECT_FALSE(pInStorage); +} + +TEST(StackStorageTest, PopErasesStorage) +{ + constexpr size_t size = 64; + StackStorage storage; + + const void* p = storage.allocate(size); + const bool storageWasFull = storage.used() == size; + + storage.deallocate(p, size); + const bool storageIsEmpty = storage.isEmpty(); + + EXPECT_TRUE(storageWasFull); + EXPECT_TRUE(storageIsEmpty); +} + +TEST(StackStorageTest, CanAllocateAfterPop) +{ + constexpr size_t size = 64; + StackStorage storage; + + const void* p = storage.allocate(1); + const bool canAllocateBeforePop = storage.canAllocate(size); + + storage.deallocate(p, 1); + const bool canAllocateAfterPop = storage.canAllocate(size); + + EXPECT_FALSE(canAllocateBeforePop); + EXPECT_TRUE(canAllocateAfterPop); +} + +TEST(StackStorageTest, PopSuportsAlign) +{ + constexpr size_t size = 64; + constexpr size_t align = 8; + StackStorage storage; + + const void* p = storage.allocate(1); + storage.deallocate(p, align); + const bool storageIsEmpty = storage.isEmpty(); + + EXPECT_TRUE(storageIsEmpty); +} + +TEST(StackStorageTest, PopInReverseOrder) +{ + constexpr size_t size = 64; + constexpr size_t align = 8; + StackStorage storage; + + const void* p1 = storage.allocate(1); + const void* p2 = storage.allocate(1); + + storage.deallocate(p2, 1); + storage.deallocate(p1, 1); + + const bool storageIsEmpty = storage.isEmpty(); + + EXPECT_TRUE(storageIsEmpty); +} + +TEST(StackStorageTest, CantPopPointerOutsideStorage) +{ + constexpr size_t size = 64; + StackStorage storage; + + EXPECT_DEBUG_DEATH(storage.deallocate(new int(0), sizeof(int)), ""); +} + +TEST(StackStorageTest, CantPopWrongSize) +{ + constexpr size_t size = 64; + StackStorage storage; + + const void* p = storage.allocate(3); + + EXPECT_DEBUG_DEATH(storage.deallocate(p, 2), ""); + EXPECT_DEBUG_DEATH(storage.deallocate(p, 5), ""); +} + +TEST(StackStorageTest, CantPopInWrongOrder) +{ + constexpr size_t size = 64; + StackStorage storage; + + const void* p1 = storage.allocate(4); + std::ignore = storage.allocate(4); + + EXPECT_DEBUG_DEATH(storage.deallocate(p1, 4), ""); +} \ No newline at end of file diff --git a/tests/TestExample.cpp b/tests/TestExample.cpp deleted file mode 100644 index 0de2fc9..0000000 --- a/tests/TestExample.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -TEST(ExampleTest, 2Plus2Equals4) -{ - EXPECT_EQ(2 + 2, 4); -} \ No newline at end of file