From 79ca6848534980df933a50d3c3c56227e7a1cb95 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 5 Sep 2023 09:21:30 +0100 Subject: [PATCH 01/15] feat(core): add ConstructibleTrait --- core/CMakeLists.txt | 2 + .../core/reflection/traits/constructible.hpp | 164 ++++++++++++++++++ .../core/reflection/traits/constructible.cpp | 87 ++++++++++ 3 files changed, 253 insertions(+) create mode 100644 core/include/cubos/core/reflection/traits/constructible.hpp create mode 100644 core/src/cubos/core/reflection/traits/constructible.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 6392d6e4db..8f25829803 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -33,6 +33,7 @@ set(CUBOS_CORE_SOURCE "src/cubos/core/memory/buffer_stream.cpp" "src/cubos/core/reflection/type.cpp" + "src/cubos/core/reflection/traits/constructible.cpp" "src/cubos/core/data/serializer.cpp" "src/cubos/core/data/deserializer.cpp" @@ -99,6 +100,7 @@ set(CUBOS_CORE_INCLUDE "include/cubos/core/reflection/reflect.hpp" "include/cubos/core/reflection/type.hpp" + "include/cubos/core/reflection/traits/constructible.hpp" "include/cubos/core/data/serializer.hpp" "include/cubos/core/data/deserializer.hpp" diff --git a/core/include/cubos/core/reflection/traits/constructible.hpp b/core/include/cubos/core/reflection/traits/constructible.hpp new file mode 100644 index 0000000000..78eb9d57a4 --- /dev/null +++ b/core/include/cubos/core/reflection/traits/constructible.hpp @@ -0,0 +1,164 @@ +/// @file +/// @brief Class @ref cubos::core::reflection::ConstructibleTrait. +/// @ingroup core-reflection + +#pragma once + +#include + +namespace cubos::core::reflection +{ + /// @brief Describes how a reflected type may be constructed and destructed. + /// @ingroup core-reflection + class ConstructibleTrait + { + public: + /// @brief Function pointer to the destructor of a type. + /// @param instance Pointer to the instance to destruct. + using Destructor = void (*)(void* instance); + + /// @brief Function pointer to the default constructor of a type. + /// @param instance Pointer to the location to construct the instance at. + using DefaultConstructor = void (*)(void* instance); + + /// @brief Function pointer to the copy constructor of a type. + /// @param instance Pointer to the location to construct the instance at. + /// @param other Pointer to the instance to copy construct from. + using CopyConstructor = void (*)(void* instance, const void* other); + + /// @brief Function pointer to the move constructor of a type. + /// @param instance Pointer to the location to construct the instance at. + /// @param other Pointer to the instance to move construct from. + using MoveConstructor = void (*)(void* instance, void* other); + + /// @brief Builder for @ref ConstructibleTrait. + template + class Builder; + + /// @brief Constructs. + /// @param size Size of the type in bytes. + /// @param alignment Alignment of the type in bytes (must be a power of two). + /// @param destructor Function pointer to the destructor of the type. + ConstructibleTrait(std::size_t size, std::size_t alignment, void (*destructor)(void*)); + + /// @brief Sets the default constructor of the type. + /// + /// Aborts if the default constructor has already been set. + /// + /// @param defaultConstructor Function pointer to the default constructor of the type. + /// @return Reference to this object. + ConstructibleTrait& withDefaultConstructor(DefaultConstructor defaultConstructor); + + /// @brief Sets the copy constructor of the type. + /// + /// Aborts if the copy constructor has already been set. + /// + /// @param copyConstructor Function pointer to the copy constructor of the type. + /// @return Reference to this object. + ConstructibleTrait& withCopyConstructor(CopyConstructor copyConstructor); + + /// @brief Sets the move constructor of the type. + /// + /// Aborts if the copy constructor has already been set. + /// + /// @param moveConstructor Function pointer to the move constructor of the type. + /// @return Reference to this object. + ConstructibleTrait& withMoveConstructor(MoveConstructor moveConstructor); + + /// @brief Returns a trait builder for the given type. + /// @tparam T Type to build a trait for. + /// @return Trait builder. + template + static Builder builder(); + + /// @brief Returns the size of the type in bytes. + /// @return Size of the type in bytes. + std::size_t size() const; + + /// @brief Returns the alignment of the type in bytes. + /// @return Alignment of the type in bytes. + std::size_t alignment() const; + + /// @brief Destructs an instance of the type. + /// @param instance Pointer to the instance to destruct. + void destruct(void* instance) const; + + /// @brief Default constructs an instance of the type. + /// @param instance Pointer to the location to construct the instance at. + /// @return Whether default construction is supported. + bool defaultConstruct(void* instance) const; + + /// @brief Copy constructs an instance of the type. + /// @param instance Pointer to the location to construct the instance at. + /// @param other Pointer to the instance to copy construct from. + /// @return Whether copy construction is supported. + bool copyConstruct(void* instance, const void* other) const; + + /// @brief Move constructs an instance of the type. + /// @param instance Pointer to the location to construct the instance at. + /// @param other Pointer to the instance to move construct from. + /// @return Whether move construction is supported. + bool moveConstruct(void* instance, void* other) const; + + private: + std::size_t mSize; + std::size_t mAlignment; + Destructor mDestructor; + DefaultConstructor mDefaultConstructor; + CopyConstructor mCopyConstructor; + MoveConstructor mMoveConstructor; + }; + + template + class ConstructibleTrait::Builder + { + public: + /// @brief Constructs. + Builder() + : mTrait(sizeof(T), alignof(T), [](void* instance) { static_cast(instance)->~T(); }) + { + } + + /// @brief Returns the constructed trait. + /// @return Constructed trait. + ConstructibleTrait build() && + { + return std::move(mTrait); + } + + /// @brief Sets the copy constructor of the type. + /// @return Reference to this object. + Builder&& withDefaultConstructor() && + { + mTrait.withDefaultConstructor([](void* instance) { new (instance) T(); }); + return std::move(*this); + } + + /// @brief Sets the copy constructor of the type. + /// @return Reference to this object. + Builder&& withCopyConstructor() && + { + mTrait.withCopyConstructor( + [](void* instance, const void* other) { new (instance) T(*static_cast(other)); }); + return std::move(*this); + } + + /// @brief Sets the move constructor of the type. + /// @return Reference to this object. + Builder&& withMoveConstructor() && + { + mTrait.withMoveConstructor( + [](void* instance, void* other) { new (instance) T(std::move(*static_cast(other))); }); + return std::move(*this); + } + + private: + ConstructibleTrait mTrait; + }; + + template + ConstructibleTrait::Builder ConstructibleTrait::builder() + { + return Builder(); + } +} // namespace cubos::core::reflection diff --git a/core/src/cubos/core/reflection/traits/constructible.cpp b/core/src/cubos/core/reflection/traits/constructible.cpp new file mode 100644 index 0000000000..d15373cae7 --- /dev/null +++ b/core/src/cubos/core/reflection/traits/constructible.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +using namespace cubos::core::reflection; + +ConstructibleTrait::ConstructibleTrait(std::size_t size, std::size_t alignment, void (*destructor)(void*)) + : mSize(size) + , mAlignment(alignment) + , mDestructor(destructor) + , mDefaultConstructor(nullptr) + , mCopyConstructor(nullptr) + , mMoveConstructor(nullptr) +{ + CUBOS_ASSERT(mAlignment > 0, "Alignment must be positive"); + CUBOS_ASSERT((mAlignment & (mAlignment - 1)) == 0, "Alignment must be a power of two"); + CUBOS_ASSERT(mDestructor, "Destructor must be non-null"); +} + +ConstructibleTrait& ConstructibleTrait::withDefaultConstructor(DefaultConstructor defaultConstructor) +{ + CUBOS_ASSERT(!mDefaultConstructor, "Default constructor already set"); + mDefaultConstructor = defaultConstructor; + return *this; +} + +ConstructibleTrait& ConstructibleTrait::withCopyConstructor(CopyConstructor copyConstructor) +{ + CUBOS_ASSERT(!mCopyConstructor, "Copy constructor already set"); + mCopyConstructor = copyConstructor; + return *this; +} + +ConstructibleTrait& ConstructibleTrait::withMoveConstructor(MoveConstructor moveConstructor) +{ + CUBOS_ASSERT(!mMoveConstructor, "Move constructor already set"); + mMoveConstructor = moveConstructor; + return *this; +} + +std::size_t ConstructibleTrait::size() const +{ + return mSize; +} + +std::size_t ConstructibleTrait::alignment() const +{ + return mAlignment; +} + +void ConstructibleTrait::destruct(void* instance) const +{ + mDestructor(instance); +} + +bool ConstructibleTrait::defaultConstruct(void* instance) const +{ + if (mDefaultConstructor) + { + mDefaultConstructor(instance); + return true; + } + + return false; +} + +bool ConstructibleTrait::copyConstruct(void* instance, const void* other) const +{ + if (mCopyConstructor) + { + mCopyConstructor(instance, other); + return true; + } + + return false; +} + +bool ConstructibleTrait::moveConstruct(void* instance, void* other) const +{ + if (mMoveConstructor) + { + mMoveConstructor(instance, other); + return true; + } + + return false; +} From ef59bf225e6db0a69aba942cb53cf8d6d941a05b Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 5 Sep 2023 09:22:15 +0100 Subject: [PATCH 02/15] test(core): cover ConstructibleTrait --- core/tests/CMakeLists.txt | 1 + .../tests/reflection/traits/constructible.cpp | 108 ++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 core/tests/reflection/traits/constructible.cpp diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index 91530b8dbd..9e17d1d332 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable( reflection/reflect.cpp reflection/type.cpp + reflection/traits/constructible.cpp data/fs/embedded_archive.cpp data/fs/standard_archive.cpp diff --git a/core/tests/reflection/traits/constructible.cpp b/core/tests/reflection/traits/constructible.cpp new file mode 100644 index 0000000000..ffeaf8cfa6 --- /dev/null +++ b/core/tests/reflection/traits/constructible.cpp @@ -0,0 +1,108 @@ +#include + +#include + +#include "../../utils.hpp" + +using cubos::core::reflection::ConstructibleTrait; + +struct SimpleConstructible +{ + static constexpr int DEFAULT = 0x12345678; + + int value; + + SimpleConstructible() + : value(DEFAULT) + { + } +}; + +TEST_CASE("reflection::ConstructibleTrait") +{ + SUBCASE("size and alignment are correct") + { + auto trait = ConstructibleTrait::builder().build(); + CHECK(trait.size() == sizeof(DetectDestructor)); + CHECK(trait.alignment() == alignof(DetectDestructor)); + } + + SUBCASE("destructor works") + { + auto ptr = operator new(sizeof(DetectDestructor)); + + auto trait = ConstructibleTrait::builder().build(); + bool destroyed = false; + auto detector = new (ptr) DetectDestructor(&destroyed); + + REQUIRE_FALSE(destroyed); + trait.destruct(detector); + CHECK(destroyed); + } + + SUBCASE("non-assigned constructors work as expected") + { + auto ptr = operator new(sizeof(DetectDestructor)); + + auto trait = ConstructibleTrait::builder().build(); + DetectDestructor detector{}; + + CHECK_FALSE(trait.defaultConstruct(ptr)); + CHECK_FALSE(trait.copyConstruct(ptr, &detector)); + CHECK_FALSE(trait.moveConstruct(ptr, &detector)); + + operator delete(ptr); + } + + SUBCASE("default constructor works") + { + auto ptr = operator new(sizeof(SimpleConstructible)); + + auto trait = ConstructibleTrait::builder().withDefaultConstructor().build(); + REQUIRE(trait.defaultConstruct(ptr)); + + auto simple = static_cast(ptr); + CHECK(simple->value == SimpleConstructible::DEFAULT); + trait.destruct(ptr); + + operator delete(ptr); + } + + SUBCASE("copy constructor works") + { + auto ptr = operator new(sizeof(SimpleConstructible)); + + auto trait = ConstructibleTrait::builder().withCopyConstructor().build(); + SimpleConstructible copied{}; + copied.value = 1; + REQUIRE(trait.copyConstruct(ptr, &copied)); + + auto simple = static_cast(ptr); + CHECK(simple->value == copied.value); + trait.destruct(ptr); + + operator delete(ptr); + } + + SUBCASE("move constructor works") + { + auto ptr = operator new(sizeof(DetectDestructor)); + + auto trait = ConstructibleTrait::builder().withMoveConstructor().build(); + bool destroyed = false; + + // The destroyed flag is moved from the 'moved' detector to the detector at 'ptr', and thus + // the flag is not set when 'moved' is destructed. + { + DetectDestructor moved{&destroyed}; + REQUIRE(trait.moveConstruct(ptr, &moved)); + REQUIRE_FALSE(destroyed); + } + + CHECK_FALSE(destroyed); + trait.destruct(ptr); + CHECK(destroyed); + + operator delete(ptr); + } +} From f8a1f1e2b4756b988366adcd34fe39d0c8ce7a2d Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 5 Sep 2023 09:22:46 +0100 Subject: [PATCH 03/15] docs(core): add reflection samples subpage --- core/samples/reflection/basic/page.md | 4 ++-- core/samples/reflection/page.md | 6 ++++++ docs/pages/3_examples/1_core/main.md | 12 ++---------- 3 files changed, 10 insertions(+), 12 deletions(-) create mode 100644 core/samples/reflection/page.md diff --git a/core/samples/reflection/basic/page.md b/core/samples/reflection/basic/page.md index 609f848392..d5b1b76ee6 100644 --- a/core/samples/reflection/basic/page.md +++ b/core/samples/reflection/basic/page.md @@ -1,6 +1,6 @@ -# Reflection {#examples-core-reflection-basic} +# Basic Usage {#examples-core-reflection-basic} -@brief Using the reflection system. +@brief Defining and using reflectable types. Lets say you have a type `Person`, which you want to be able to reflect. You can declare it as reflectable using the macro @ref CUBOS_REFLECT, for example, diff --git a/core/samples/reflection/page.md b/core/samples/reflection/page.md new file mode 100644 index 0000000000..375022098f --- /dev/null +++ b/core/samples/reflection/page.md @@ -0,0 +1,6 @@ +# Reflection {#examples-core-reflection} + +@brief How to use the reflection system. + +- @subpage examples-core-reflection-basic - @copybrief examples-core-reflection-basic +- @subpage examples-core-reflection-traits-constructible - @copybrief examples-core-reflection-traits-constructible diff --git a/docs/pages/3_examples/1_core/main.md b/docs/pages/3_examples/1_core/main.md index 20238c7775..9a48f9137e 100644 --- a/docs/pages/3_examples/1_core/main.md +++ b/docs/pages/3_examples/1_core/main.md @@ -6,15 +6,7 @@ you plan to work on the engine itself, it's possible, if not probable, that you will need to use some of the features of the @ref core library directly. -## Fully documented examples - The following examples have fully documented tutorials: -- @subpage examples-core-logging - @copybrief examples-core-logging -- @subpage examples-core-reflection-basic - @copybrief examples-core-reflection-basic - -## Undocumented examples -The following examples are not yet documented, but may still be useful to learn -some of the features: - -@note Under construction. +- @subpage examples-core-logging - @copybrief examples-core-logging +- @subpage examples-core-reflection - @copybrief examples-core-reflection From bae8555252a4c4b0189243ec4bda08e0ea2fd3eb Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 5 Sep 2023 09:23:05 +0100 Subject: [PATCH 04/15] docs(core): add ConstructibleTrait sample --- core/samples/CMakeLists.txt | 1 + .../reflection/traits/constructible/main.cpp | 48 +++++++++++++++++++ .../reflection/traits/constructible/page.md | 44 +++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 core/samples/reflection/traits/constructible/main.cpp create mode 100644 core/samples/reflection/traits/constructible/page.md diff --git a/core/samples/CMakeLists.txt b/core/samples/CMakeLists.txt index 72321b4b00..c8b7dc32e2 100644 --- a/core/samples/CMakeLists.txt +++ b/core/samples/CMakeLists.txt @@ -28,6 +28,7 @@ endmacro() make_sample(DIR "logging") make_sample(DIR "reflection/basic") +make_sample(DIR "reflection/traits/constructible") make_sample(DIR "data/fs/embedded_archive" SOURCES "embed.cpp") make_sample(DIR "data/fs/standard_archive") make_sample(DIR "data/serialization") diff --git a/core/samples/reflection/traits/constructible/main.cpp b/core/samples/reflection/traits/constructible/main.cpp new file mode 100644 index 0000000000..d16fcdd2c3 --- /dev/null +++ b/core/samples/reflection/traits/constructible/main.cpp @@ -0,0 +1,48 @@ +#include + +/// [Scale declaration] +#include + +struct Scale +{ + CUBOS_REFLECT; + float value = 1.0F; +}; +/// [Scale declaration] + +/// [Scale definition] +#include +#include + +using cubos::core::reflection::ConstructibleTrait; +using cubos::core::reflection::Type; + +CUBOS_REFLECT_IMPL(Scale) +{ + return Type::create("Scale").with(ConstructibleTrait::builder().withDefaultConstructor().build()); +} +/// [Scale definition] + +/// [Accessing the trait] +int main() +{ + using cubos::core::reflection::reflect; + + const auto& scaleType = reflect(); + CUBOS_ASSERT(scaleType.has()); + const auto& constructible = scaleType.get(); + /// [Accessing the trait] + + /// [Creating a default instance] + // Allocate memory for the instance and default-construct it. + void* instance = operator new(constructible.size()); + CUBOS_ASSERT(constructible.defaultConstruct(instance)); + CUBOS_ASSERT(static_cast(instance)->value == 1.0F); + /// [Creating a default instance] + + /// [Destroying the instance] + // Destroy the instance and deallocate its memory. + constructible.destruct(instance); + operator delete(instance); +} +/// [Destroying the instance] diff --git a/core/samples/reflection/traits/constructible/page.md b/core/samples/reflection/traits/constructible/page.md new file mode 100644 index 0000000000..31b2237018 --- /dev/null +++ b/core/samples/reflection/traits/constructible/page.md @@ -0,0 +1,44 @@ +# Constructible Trait {#examples-core-reflection-traits-constructible} + +@brief Exposing the destructor and constructors of a type. + +You may find it useful while working with type-erased data to be able to create +copies of the data, destroy it, or move it around. The +@ref cubos::core::reflection::ConstructibleTrait "ConstructibleTrait" trait +exposes the size, alignment, destructor and constructors of a type. + +Lets say you have a type `Scale`, which you want to be able to reflect, with a +default value of `1.0`: + +@snippet reflection/traits/constructible/main.cpp Scale declaration + +We're going to add the @ref cubos::core::reflection::ConstructibleTrait +"ConstructibleTrait" trait to it, so that we can create instances of it at +runtime: + +@snippet reflection/traits/constructible/main.cpp Scale definition + +Now, we can access the trait from the reflected type: + +@snippet reflection/traits/constructible/main.cpp Accessing the trait + +Imagine for a moment that you don't know the type of the data you're working, +and you only have access to its reflection data through `scaleType`. If you +want to create a default instance of the type, you can call the default +constructor stored in the trait: + +@snippet reflection/traits/constructible/main.cpp Creating a default instance + +The @ref cubos::core::reflection::ConstructibleTrait::defaultConstruct +"defaultConstruct" method returns a boolean which indicates if the type has a +default constructor or not. In this case, since we added the default +constructor to the trait, it will return `true`. + +This could be useful, for example, to fallback from using `moveConstruct` to +`copyConstruct`, if the first isn't available. + +Don't forget to destroy the instance manually when you're done with it: + +@snippet reflection/traits/constructible/main.cpp Destroying the instance + + From 4e46c4cabe2d88053a65d244ab2a778550b77d48 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sun, 10 Sep 2023 16:41:33 +0100 Subject: [PATCH 05/15] refactor(core): remove vector include --- .../cubos/core/reflection/traits/constructible.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/constructible.hpp b/core/include/cubos/core/reflection/traits/constructible.hpp index 78eb9d57a4..e2e6c87eb9 100644 --- a/core/include/cubos/core/reflection/traits/constructible.hpp +++ b/core/include/cubos/core/reflection/traits/constructible.hpp @@ -4,7 +4,7 @@ #pragma once -#include +#include namespace cubos::core::reflection { @@ -123,7 +123,7 @@ namespace cubos::core::reflection /// @return Constructed trait. ConstructibleTrait build() && { - return std::move(mTrait); + return mTrait; } /// @brief Sets the copy constructor of the type. @@ -131,7 +131,7 @@ namespace cubos::core::reflection Builder&& withDefaultConstructor() && { mTrait.withDefaultConstructor([](void* instance) { new (instance) T(); }); - return std::move(*this); + return static_cast(*this); } /// @brief Sets the copy constructor of the type. @@ -140,7 +140,7 @@ namespace cubos::core::reflection { mTrait.withCopyConstructor( [](void* instance, const void* other) { new (instance) T(*static_cast(other)); }); - return std::move(*this); + return static_cast(*this); } /// @brief Sets the move constructor of the type. @@ -148,8 +148,8 @@ namespace cubos::core::reflection Builder&& withMoveConstructor() && { mTrait.withMoveConstructor( - [](void* instance, void* other) { new (instance) T(std::move(*static_cast(other))); }); - return std::move(*this); + [](void* instance, void* other) { new (instance) T(static_cast(*static_cast(other))); }); + return static_cast(*this); } private: From 8e6576784a2ab139487bad961b074603a429c730 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sun, 10 Sep 2023 17:37:06 +0100 Subject: [PATCH 06/15] docs(core): fix ConstructibleTrait return docs --- core/include/cubos/core/reflection/traits/constructible.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/constructible.hpp b/core/include/cubos/core/reflection/traits/constructible.hpp index e2e6c87eb9..5fd7efb0a6 100644 --- a/core/include/cubos/core/reflection/traits/constructible.hpp +++ b/core/include/cubos/core/reflection/traits/constructible.hpp @@ -127,7 +127,7 @@ namespace cubos::core::reflection } /// @brief Sets the copy constructor of the type. - /// @return Reference to this object. + /// @return Builder. Builder&& withDefaultConstructor() && { mTrait.withDefaultConstructor([](void* instance) { new (instance) T(); }); @@ -135,7 +135,7 @@ namespace cubos::core::reflection } /// @brief Sets the copy constructor of the type. - /// @return Reference to this object. + /// @return Builder. Builder&& withCopyConstructor() && { mTrait.withCopyConstructor( @@ -144,7 +144,7 @@ namespace cubos::core::reflection } /// @brief Sets the move constructor of the type. - /// @return Reference to this object. + /// @return Builder. Builder&& withMoveConstructor() && { mTrait.withMoveConstructor( From 3f66f25c845530febe02f9b8bbb3f402402dc82c Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sun, 10 Sep 2023 18:28:03 +0100 Subject: [PATCH 07/15] docs(core): link sample in ConstructibleTrait docs --- core/include/cubos/core/reflection/traits/constructible.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/include/cubos/core/reflection/traits/constructible.hpp b/core/include/cubos/core/reflection/traits/constructible.hpp index 5fd7efb0a6..d2c943c910 100644 --- a/core/include/cubos/core/reflection/traits/constructible.hpp +++ b/core/include/cubos/core/reflection/traits/constructible.hpp @@ -9,6 +9,8 @@ namespace cubos::core::reflection { /// @brief Describes how a reflected type may be constructed and destructed. + /// @see See @ref examples-core-reflection-traits-constructible for an example of using this + /// trait. /// @ingroup core-reflection class ConstructibleTrait { From ed7ef03a32919fceffa792b84adfd6f286061f52 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sun, 10 Sep 2023 18:33:49 +0100 Subject: [PATCH 08/15] style(core): move static method in ConstructibleTrait --- .../cubos/core/reflection/traits/constructible.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/constructible.hpp b/core/include/cubos/core/reflection/traits/constructible.hpp index d2c943c910..fc3b22047e 100644 --- a/core/include/cubos/core/reflection/traits/constructible.hpp +++ b/core/include/cubos/core/reflection/traits/constructible.hpp @@ -43,6 +43,12 @@ namespace cubos::core::reflection /// @param destructor Function pointer to the destructor of the type. ConstructibleTrait(std::size_t size, std::size_t alignment, void (*destructor)(void*)); + /// @brief Returns a trait builder for the given type. + /// @tparam T Type to build a trait for. + /// @return Trait builder. + template + static Builder builder(); + /// @brief Sets the default constructor of the type. /// /// Aborts if the default constructor has already been set. @@ -67,12 +73,6 @@ namespace cubos::core::reflection /// @return Reference to this object. ConstructibleTrait& withMoveConstructor(MoveConstructor moveConstructor); - /// @brief Returns a trait builder for the given type. - /// @tparam T Type to build a trait for. - /// @return Trait builder. - template - static Builder builder(); - /// @brief Returns the size of the type in bytes. /// @return Size of the type in bytes. std::size_t size() const; From d2715125cfa1d6b587edee74112f838c5fe2ef9a Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 12 Sep 2023 23:18:48 +0100 Subject: [PATCH 09/15] refactor(core): move this in ConstructibleTrait --- .../core/reflection/traits/constructible.hpp | 23 ++++++++++--------- .../reflection/traits/constructible/main.cpp | 2 +- .../core/reflection/traits/constructible.cpp | 12 +++++----- .../tests/reflection/traits/constructible.cpp | 14 ++++++----- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/constructible.hpp b/core/include/cubos/core/reflection/traits/constructible.hpp index fc3b22047e..2868c792e7 100644 --- a/core/include/cubos/core/reflection/traits/constructible.hpp +++ b/core/include/cubos/core/reflection/traits/constructible.hpp @@ -47,31 +47,31 @@ namespace cubos::core::reflection /// @tparam T Type to build a trait for. /// @return Trait builder. template - static Builder builder(); + static Builder typed(); /// @brief Sets the default constructor of the type. /// /// Aborts if the default constructor has already been set. /// /// @param defaultConstructor Function pointer to the default constructor of the type. - /// @return Reference to this object. - ConstructibleTrait& withDefaultConstructor(DefaultConstructor defaultConstructor); + /// @return Trait. + ConstructibleTrait&& withDefaultConstructor(DefaultConstructor defaultConstructor) &&; /// @brief Sets the copy constructor of the type. /// /// Aborts if the copy constructor has already been set. /// /// @param copyConstructor Function pointer to the copy constructor of the type. - /// @return Reference to this object. - ConstructibleTrait& withCopyConstructor(CopyConstructor copyConstructor); + /// @return Trait. + ConstructibleTrait&& withCopyConstructor(CopyConstructor copyConstructor) &&; /// @brief Sets the move constructor of the type. /// /// Aborts if the copy constructor has already been set. /// /// @param moveConstructor Function pointer to the move constructor of the type. - /// @return Reference to this object. - ConstructibleTrait& withMoveConstructor(MoveConstructor moveConstructor); + /// @return Trait. + ConstructibleTrait&& withMoveConstructor(MoveConstructor moveConstructor) &&; /// @brief Returns the size of the type in bytes. /// @return Size of the type in bytes. @@ -132,7 +132,8 @@ namespace cubos::core::reflection /// @return Builder. Builder&& withDefaultConstructor() && { - mTrait.withDefaultConstructor([](void* instance) { new (instance) T(); }); + mTrait = static_cast(mTrait).withDefaultConstructor( + [](void* instance) { new (instance) T(); }); return static_cast(*this); } @@ -140,7 +141,7 @@ namespace cubos::core::reflection /// @return Builder. Builder&& withCopyConstructor() && { - mTrait.withCopyConstructor( + mTrait = static_cast(mTrait).withCopyConstructor( [](void* instance, const void* other) { new (instance) T(*static_cast(other)); }); return static_cast(*this); } @@ -149,7 +150,7 @@ namespace cubos::core::reflection /// @return Builder. Builder&& withMoveConstructor() && { - mTrait.withMoveConstructor( + mTrait = static_cast(mTrait).withMoveConstructor( [](void* instance, void* other) { new (instance) T(static_cast(*static_cast(other))); }); return static_cast(*this); } @@ -159,7 +160,7 @@ namespace cubos::core::reflection }; template - ConstructibleTrait::Builder ConstructibleTrait::builder() + ConstructibleTrait::Builder ConstructibleTrait::typed() { return Builder(); } diff --git a/core/samples/reflection/traits/constructible/main.cpp b/core/samples/reflection/traits/constructible/main.cpp index d16fcdd2c3..4e6780f33d 100644 --- a/core/samples/reflection/traits/constructible/main.cpp +++ b/core/samples/reflection/traits/constructible/main.cpp @@ -19,7 +19,7 @@ using cubos::core::reflection::Type; CUBOS_REFLECT_IMPL(Scale) { - return Type::create("Scale").with(ConstructibleTrait::builder().withDefaultConstructor().build()); + return Type::create("Scale").with(ConstructibleTrait::typed().withDefaultConstructor().build()); } /// [Scale definition] diff --git a/core/src/cubos/core/reflection/traits/constructible.cpp b/core/src/cubos/core/reflection/traits/constructible.cpp index d15373cae7..5078d368a4 100644 --- a/core/src/cubos/core/reflection/traits/constructible.cpp +++ b/core/src/cubos/core/reflection/traits/constructible.cpp @@ -17,25 +17,25 @@ ConstructibleTrait::ConstructibleTrait(std::size_t size, std::size_t alignment, CUBOS_ASSERT(mDestructor, "Destructor must be non-null"); } -ConstructibleTrait& ConstructibleTrait::withDefaultConstructor(DefaultConstructor defaultConstructor) +ConstructibleTrait&& ConstructibleTrait::withDefaultConstructor(DefaultConstructor defaultConstructor) && { CUBOS_ASSERT(!mDefaultConstructor, "Default constructor already set"); mDefaultConstructor = defaultConstructor; - return *this; + return static_cast(*this); } -ConstructibleTrait& ConstructibleTrait::withCopyConstructor(CopyConstructor copyConstructor) +ConstructibleTrait&& ConstructibleTrait::withCopyConstructor(CopyConstructor copyConstructor) && { CUBOS_ASSERT(!mCopyConstructor, "Copy constructor already set"); mCopyConstructor = copyConstructor; - return *this; + return static_cast(*this); } -ConstructibleTrait& ConstructibleTrait::withMoveConstructor(MoveConstructor moveConstructor) +ConstructibleTrait&& ConstructibleTrait::withMoveConstructor(MoveConstructor moveConstructor) && { CUBOS_ASSERT(!mMoveConstructor, "Move constructor already set"); mMoveConstructor = moveConstructor; - return *this; + return static_cast(*this); } std::size_t ConstructibleTrait::size() const diff --git a/core/tests/reflection/traits/constructible.cpp b/core/tests/reflection/traits/constructible.cpp index ffeaf8cfa6..c04101b16e 100644 --- a/core/tests/reflection/traits/constructible.cpp +++ b/core/tests/reflection/traits/constructible.cpp @@ -22,7 +22,7 @@ TEST_CASE("reflection::ConstructibleTrait") { SUBCASE("size and alignment are correct") { - auto trait = ConstructibleTrait::builder().build(); + auto trait = ConstructibleTrait::typed().build(); CHECK(trait.size() == sizeof(DetectDestructor)); CHECK(trait.alignment() == alignof(DetectDestructor)); } @@ -31,20 +31,22 @@ TEST_CASE("reflection::ConstructibleTrait") { auto ptr = operator new(sizeof(DetectDestructor)); - auto trait = ConstructibleTrait::builder().build(); + auto trait = ConstructibleTrait::typed().build(); bool destroyed = false; auto detector = new (ptr) DetectDestructor(&destroyed); REQUIRE_FALSE(destroyed); trait.destruct(detector); CHECK(destroyed); + + operator delete(ptr); } SUBCASE("non-assigned constructors work as expected") { auto ptr = operator new(sizeof(DetectDestructor)); - auto trait = ConstructibleTrait::builder().build(); + auto trait = ConstructibleTrait::typed().build(); DetectDestructor detector{}; CHECK_FALSE(trait.defaultConstruct(ptr)); @@ -58,7 +60,7 @@ TEST_CASE("reflection::ConstructibleTrait") { auto ptr = operator new(sizeof(SimpleConstructible)); - auto trait = ConstructibleTrait::builder().withDefaultConstructor().build(); + auto trait = ConstructibleTrait::typed().withDefaultConstructor().build(); REQUIRE(trait.defaultConstruct(ptr)); auto simple = static_cast(ptr); @@ -72,7 +74,7 @@ TEST_CASE("reflection::ConstructibleTrait") { auto ptr = operator new(sizeof(SimpleConstructible)); - auto trait = ConstructibleTrait::builder().withCopyConstructor().build(); + auto trait = ConstructibleTrait::typed().withCopyConstructor().build(); SimpleConstructible copied{}; copied.value = 1; REQUIRE(trait.copyConstruct(ptr, &copied)); @@ -88,7 +90,7 @@ TEST_CASE("reflection::ConstructibleTrait") { auto ptr = operator new(sizeof(DetectDestructor)); - auto trait = ConstructibleTrait::builder().withMoveConstructor().build(); + auto trait = ConstructibleTrait::typed().withMoveConstructor().build(); bool destroyed = false; // The destroyed flag is moved from the 'moved' detector to the detector at 'ptr', and thus From 9be84464c41722d4a16675b294a953982d31d55c Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Thu, 14 Sep 2023 18:56:38 +0100 Subject: [PATCH 10/15] docs(core): add traits module.dox --- core/include/cubos/core/reflection/traits/module.dox | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 core/include/cubos/core/reflection/traits/module.dox diff --git a/core/include/cubos/core/reflection/traits/module.dox b/core/include/cubos/core/reflection/traits/module.dox new file mode 100644 index 0000000000..277c26d4a8 --- /dev/null +++ b/core/include/cubos/core/reflection/traits/module.dox @@ -0,0 +1,2 @@ +/// @dir +/// @brief @ref core-reflection module built-in traits. From 2876879987d507ebcd94547b57a4d46580b0108c Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sat, 16 Sep 2023 17:56:23 +0100 Subject: [PATCH 11/15] feat(core): add constructible_utils.hpp --- .../reflection/traits/constructible_utils.hpp | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 core/include/cubos/core/reflection/traits/constructible_utils.hpp diff --git a/core/include/cubos/core/reflection/traits/constructible_utils.hpp b/core/include/cubos/core/reflection/traits/constructible_utils.hpp new file mode 100644 index 0000000000..1864ebe000 --- /dev/null +++ b/core/include/cubos/core/reflection/traits/constructible_utils.hpp @@ -0,0 +1,42 @@ +/// @file +/// @brief Utilities for @ref cubos::core::reflection::ConstructibleTrait. +/// @note Avoid using this header as it includes ``. +/// @ingroup core-reflection + +#pragma once + +#include + +#include + +namespace cubos::core::reflection +{ + /// @brief Returns a ConstructibleTrait with the default, copy and move constructors, set only + /// if the type has them. + /// @warning Avoid using this function as its header includes ``. + /// @tparam T Type. + /// @return ConstructibleTrait. + /// @ingroup core-reflection + template + ConstructibleTrait autoConstructibleTrait() + { + auto builder = ConstructibleTrait::typed(); + + if (std::is_default_constructible::value) + { + builder = static_cast&&>(builder).withDefaultConstructor(); + } + + if (std::is_copy_constructible::value) + { + builder = static_cast&&>(builder).withCopyConstructor(); + } + + if (std::is_move_constructible::value) + { + builder = static_cast&&>(builder).withMoveConstructor(); + } + + return static_cast&&>(builder).build(); + } +} // namespace cubos::core::reflection From ac4f02d0695d02d39167c978845b1d2919f4d3cf Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sun, 17 Sep 2023 17:15:27 +0100 Subject: [PATCH 12/15] fix(core): add missing constexpr --- .../cubos/core/reflection/traits/constructible_utils.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/constructible_utils.hpp b/core/include/cubos/core/reflection/traits/constructible_utils.hpp index 1864ebe000..7b9e9a45cc 100644 --- a/core/include/cubos/core/reflection/traits/constructible_utils.hpp +++ b/core/include/cubos/core/reflection/traits/constructible_utils.hpp @@ -22,17 +22,17 @@ namespace cubos::core::reflection { auto builder = ConstructibleTrait::typed(); - if (std::is_default_constructible::value) + if constexpr (std::is_default_constructible::value) { builder = static_cast&&>(builder).withDefaultConstructor(); } - if (std::is_copy_constructible::value) + if constexpr (std::is_copy_constructible::value) { builder = static_cast&&>(builder).withCopyConstructor(); } - if (std::is_move_constructible::value) + if constexpr (std::is_move_constructible::value) { builder = static_cast&&>(builder).withMoveConstructor(); } From 9929e702c1f8350741018fe177e1919c1d2d280c Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 19 Sep 2023 17:15:28 +0100 Subject: [PATCH 13/15] docs(core): explain what type-erased means --- core/include/cubos/core/reflection/module.dox | 7 +++++++ core/samples/reflection/page.md | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/include/cubos/core/reflection/module.dox b/core/include/cubos/core/reflection/module.dox index 14fae76fa9..a47f871f7e 100644 --- a/core/include/cubos/core/reflection/module.dox +++ b/core/include/cubos/core/reflection/module.dox @@ -10,4 +10,11 @@ namespace cubos::core::reflection /// @defgroup core-reflection Reflection /// @ingroup core /// @brief Provides utilities useful for handling type-erased data. + /// @see Take a look at the @ref examples-core-reflection examples for demonstrations of this + /// module. + /// + /// @note By type-erased data, we mean that data is stored in a generic container, for example, + /// through a void pointer, such that its type is not known at compile-time. This could be + /// useful, for example, to integrate scripts into the engine, since types defined there are + /// only know when the application is running. } // namespace cubos::core::reflection diff --git a/core/samples/reflection/page.md b/core/samples/reflection/page.md index 375022098f..b362f80a10 100644 --- a/core/samples/reflection/page.md +++ b/core/samples/reflection/page.md @@ -1,6 +1,6 @@ # Reflection {#examples-core-reflection} -@brief How to use the reflection system. +@brief Using the @ref core-reflection module. - @subpage examples-core-reflection-basic - @copybrief examples-core-reflection-basic - @subpage examples-core-reflection-traits-constructible - @copybrief examples-core-reflection-traits-constructible From 05efbd21ba038b3f638a1e936f3c1c0be0e7fdaa Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 19 Sep 2023 17:32:34 +0100 Subject: [PATCH 14/15] feat(core): add memory::move --- core/CMakeLists.txt | 1 + core/include/cubos/core/memory/move.hpp | 44 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 core/include/cubos/core/memory/move.hpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8f25829803..45c603cb09 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -91,6 +91,7 @@ set(CUBOS_CORE_INCLUDE "include/cubos/core/settings.hpp" "include/cubos/core/thread_pool.hpp" + "include/cubos/core/memory/move.hpp" "include/cubos/core/memory/stream.hpp" "include/cubos/core/memory/standard_stream.hpp" "include/cubos/core/memory/buffer_stream.hpp" diff --git a/core/include/cubos/core/memory/move.hpp b/core/include/cubos/core/memory/move.hpp new file mode 100644 index 0000000000..ec16408ca4 --- /dev/null +++ b/core/include/cubos/core/memory/move.hpp @@ -0,0 +1,44 @@ +/// @file +/// @brief Function @ref cubos::core::memory::move. +/// @ingroup core-memory + +#pragma once + +namespace cubos::core::memory +{ + /// @brief Provides a type which is the same as the given type, but without any references. + /// @note This is a replacement for `std::remove_reference`, which allows us to avoid including + /// the entire `` header. + /// @tparam T + template + struct RemoveReference + { + /// @brief Type without references. + using Type = T; + }; + + template + struct RemoveReference + { + using Type = T; + }; + + template + struct RemoveReference + { + using Type = T; + }; + + /// @brief Returns an R-value reference to the given value + /// @note This is a replacement for `std::move`, which allows us to avoid including the entire + /// `` header. + /// @tparam T Value type. + /// @param value Value to move. + /// @return Moved value. + /// @ingroup core-memory + template + typename RemoveReference::Type&& move(T&& value) + { + return static_cast::Type&&>(value); + } +} // namespace cubos::core::memory From 456ae3b62e695e615deb851b7dacc9b3ee8b8481 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 19 Sep 2023 17:32:49 +0100 Subject: [PATCH 15/15] refactor(core): use memory::move instead of static_cast --- .../core/reflection/traits/constructible.hpp | 17 +++++++++-------- .../reflection/traits/constructible_utils.hpp | 8 ++++---- .../core/reflection/traits/constructible.cpp | 6 +++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/constructible.hpp b/core/include/cubos/core/reflection/traits/constructible.hpp index 2868c792e7..a46fde7da7 100644 --- a/core/include/cubos/core/reflection/traits/constructible.hpp +++ b/core/include/cubos/core/reflection/traits/constructible.hpp @@ -6,6 +6,8 @@ #include +#include + namespace cubos::core::reflection { /// @brief Describes how a reflected type may be constructed and destructed. @@ -132,27 +134,26 @@ namespace cubos::core::reflection /// @return Builder. Builder&& withDefaultConstructor() && { - mTrait = static_cast(mTrait).withDefaultConstructor( - [](void* instance) { new (instance) T(); }); - return static_cast(*this); + mTrait = memory::move(mTrait).withDefaultConstructor([](void* instance) { new (instance) T(); }); + return memory::move(*this); } /// @brief Sets the copy constructor of the type. /// @return Builder. Builder&& withCopyConstructor() && { - mTrait = static_cast(mTrait).withCopyConstructor( + mTrait = memory::move(mTrait).withCopyConstructor( [](void* instance, const void* other) { new (instance) T(*static_cast(other)); }); - return static_cast(*this); + return memory::move(*this); } /// @brief Sets the move constructor of the type. /// @return Builder. Builder&& withMoveConstructor() && { - mTrait = static_cast(mTrait).withMoveConstructor( - [](void* instance, void* other) { new (instance) T(static_cast(*static_cast(other))); }); - return static_cast(*this); + mTrait = memory::move(mTrait).withMoveConstructor( + [](void* instance, void* other) { new (instance) T(memory::move(*static_cast(other))); }); + return memory::move(*this); } private: diff --git a/core/include/cubos/core/reflection/traits/constructible_utils.hpp b/core/include/cubos/core/reflection/traits/constructible_utils.hpp index 7b9e9a45cc..90e0cd6f55 100644 --- a/core/include/cubos/core/reflection/traits/constructible_utils.hpp +++ b/core/include/cubos/core/reflection/traits/constructible_utils.hpp @@ -24,19 +24,19 @@ namespace cubos::core::reflection if constexpr (std::is_default_constructible::value) { - builder = static_cast&&>(builder).withDefaultConstructor(); + builder = memory::move(builder).withDefaultConstructor(); } if constexpr (std::is_copy_constructible::value) { - builder = static_cast&&>(builder).withCopyConstructor(); + builder = memory::move(builder).withCopyConstructor(); } if constexpr (std::is_move_constructible::value) { - builder = static_cast&&>(builder).withMoveConstructor(); + builder = memory::move(builder).withMoveConstructor(); } - return static_cast&&>(builder).build(); + return memory::move(builder).build(); } } // namespace cubos::core::reflection diff --git a/core/src/cubos/core/reflection/traits/constructible.cpp b/core/src/cubos/core/reflection/traits/constructible.cpp index 5078d368a4..17fd8b4ca9 100644 --- a/core/src/cubos/core/reflection/traits/constructible.cpp +++ b/core/src/cubos/core/reflection/traits/constructible.cpp @@ -21,21 +21,21 @@ ConstructibleTrait&& ConstructibleTrait::withDefaultConstructor(DefaultConstruct { CUBOS_ASSERT(!mDefaultConstructor, "Default constructor already set"); mDefaultConstructor = defaultConstructor; - return static_cast(*this); + return memory::move(*this); } ConstructibleTrait&& ConstructibleTrait::withCopyConstructor(CopyConstructor copyConstructor) && { CUBOS_ASSERT(!mCopyConstructor, "Copy constructor already set"); mCopyConstructor = copyConstructor; - return static_cast(*this); + return memory::move(*this); } ConstructibleTrait&& ConstructibleTrait::withMoveConstructor(MoveConstructor moveConstructor) && { CUBOS_ASSERT(!mMoveConstructor, "Move constructor already set"); mMoveConstructor = moveConstructor; - return static_cast(*this); + return memory::move(*this); } std::size_t ConstructibleTrait::size() const