From f5c63a50568d563dfbc5c10f3ca4df973143a4de Mon Sep 17 00:00:00 2001 From: SrGesus <108523575+SrGesus@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:15:52 +0000 Subject: [PATCH] feat(data): Allow wrapped fields to be read as normal object with a single field --- core/CMakeLists.txt | 1 + core/include/cubos/core/ecs/reflection.hpp | 15 ++++- .../cubos/core/reflection/traits/wrapper.hpp | 55 +++++++++++++++++++ core/src/data/des/json.cpp | 20 +++---- core/src/data/ser/json.cpp | 24 ++++---- core/src/reflection/traits/wrapper.cpp | 11 ++++ 6 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 core/include/cubos/core/reflection/traits/wrapper.hpp create mode 100644 core/src/reflection/traits/wrapper.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e89c6b19dc..df1e5102e9 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -60,6 +60,7 @@ set(CUBOS_CORE_SOURCE "src/reflection/traits/enum.cpp" "src/reflection/traits/mask.cpp" "src/reflection/traits/inherits.cpp" + "src/reflection/traits/wrapper.cpp" "src/reflection/external/primitives.cpp" "src/reflection/external/cstring.cpp" "src/reflection/external/string.cpp" diff --git a/core/include/cubos/core/ecs/reflection.hpp b/core/include/cubos/core/ecs/reflection.hpp index 48024d13dc..781b77ece6 100644 --- a/core/include/cubos/core/ecs/reflection.hpp +++ b/core/include/cubos/core/ecs/reflection.hpp @@ -6,6 +6,7 @@ #include #include +#include #include namespace cubos::core::ecs @@ -88,7 +89,7 @@ namespace cubos::core::ecs /// @param pointer Field pointer. /// @return Builder. template - TypeBuilder&& withField(std::string name, F T::*pointer) && + TypeBuilder&& withField(std::string name, F T::* pointer) && { mFields.addField(std::move(name), pointer); return std::move(*this); @@ -102,6 +103,18 @@ namespace cubos::core::ecs return mType; } + /// @brief Creates a type with a single field. + /// @tparam F Field type. + /// @param pointer Field pointer. + /// @return Type. + template + reflection::Type& wrap(F T::* pointer) && + { + CUBOS_ASSERT(mFields.size() == 0); + mType.with(reflection::WrapperTrait(pointer)); + return mType; + } + private: reflection::Type& mType; reflection::FieldsTrait mFields; diff --git a/core/include/cubos/core/reflection/traits/wrapper.hpp b/core/include/cubos/core/reflection/traits/wrapper.hpp new file mode 100644 index 0000000000..57746d2951 --- /dev/null +++ b/core/include/cubos/core/reflection/traits/wrapper.hpp @@ -0,0 +1,55 @@ +/// @file +/// @brief Class @ref cubos::core::reflection::WrapperTrait. +/// @ingroup core-reflection + +#pragma once + +#include +#include + +#include +#include +#include + +namespace cubos::core::reflection +{ + /// @brief Describes the single field of a reflected type. + /// @ingroup core-reflection + class CUBOS_CORE_API WrapperTrait + { + public: + CUBOS_REFLECT; + + ~WrapperTrait() + { + delete mAddressOf; + }; + + /// @brief Constructs. + template + WrapperTrait(F O::* pointer) + : mType(reflect()) + , mAddressOf(new FieldsTrait::AddressOfImpl(pointer)) + { + } + + /// @brief Gets the type of the single field. + /// @return Type. + const Type& type() const + { + return mType; + } + + /// @brief Gets a pointer to the field on a given instance of the reflected type. + /// @param instance Pointer to the instance. + /// @return Pointer to the field on the given instance. + void* value(const void* instance) const + { + return reinterpret_cast(this->mAddressOf->get(instance)); + } + + private: + const Type& mType; + const FieldsTrait::AddressOf* mAddressOf; + }; +} // namespace cubos::core::reflection \ No newline at end of file diff --git a/core/src/data/des/json.cpp b/core/src/data/des/json.cpp index c5e66d3e90..6ab2e83e70 100644 --- a/core/src/data/des/json.cpp +++ b/core/src/data/des/json.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ using cubos::core::reflection::FieldsTrait; using cubos::core::reflection::NullableTrait; using cubos::core::reflection::StringConversionTrait; using cubos::core::reflection::Type; +using cubos::core::reflection::WrapperTrait; // NOLINTBEGIN(bugprone-macro-parentheses) #define AUTO_HOOK(T, fromString) \ @@ -120,6 +122,12 @@ bool JSONDeserializer::decompose(const Type& type, void* value) return true; } + if (type.has()) + { + const auto& wrapper = type.get(); + return this->read(wrapper.type(), wrapper.value(value)); + } + if (type.has() && mIterator->is_null()) { const auto& trait = type.get(); @@ -242,18 +250,6 @@ bool JSONDeserializer::decompose(const Type& type, void* value) const auto& trait = type.get(); auto view = trait.view(value); - if (trait.size() == 1) - { - // If there's a single field, read it directly. - if (!this->read(trait.begin()->type(), view.begin()->value)) - { - CUBOS_WARN("Couldn't deserialize wrapped field {}", trait.begin()->name()); - return false; - } - - return true; - } - if (!mIterator->is_object()) { CUBOS_WARN("Expected an object of type {}, found a {}", type.name(), mIterator->type_name()); diff --git a/core/src/data/ser/json.cpp b/core/src/data/ser/json.cpp index 92538b1969..74c46e09c2 100644 --- a/core/src/data/ser/json.cpp +++ b/core/src/data/ser/json.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,7 @@ using cubos::core::reflection::NullableTrait; using cubos::core::reflection::reflect; using cubos::core::reflection::StringConversionTrait; using cubos::core::reflection::Type; +using cubos::core::reflection::WrapperTrait; // Macro used to reduce code duplication for primitive type hooks. #define AUTO_HOOK(type, ...) \ @@ -65,6 +67,12 @@ nlohmann::json JSONSerializer::output() bool JSONSerializer::decompose(const Type& type, const void* value) { + if (type.has()) + { + const auto& wrapper = type.get(); + return this->write(wrapper.type(), wrapper.value(value)); + } + if (type.has()) { const auto& trait = type.get(); @@ -112,21 +120,11 @@ bool JSONSerializer::decompose(const Type& type, const void* value) if (type.has()) { - if (type.get().size() == 1) - { - // If there's a single field, write it directly. - if (!this->write(type.get().begin()->type(), - type.get().view(value).begin()->value)) - { - CUBOS_WARN("Couldn't serialize wrapped field {}", type.get().begin()->name()); - return false; - } - - return true; - } + const auto& trait = type.get(); + auto view = trait.view(value); auto jsonObj = nlohmann::json::object(); - for (const auto& [field, fieldValue] : type.get().view(value)) + for (const auto& [field, fieldValue] : view) { if (!this->write(field->type(), fieldValue)) { diff --git a/core/src/reflection/traits/wrapper.cpp b/core/src/reflection/traits/wrapper.cpp new file mode 100644 index 0000000000..b1627c4140 --- /dev/null +++ b/core/src/reflection/traits/wrapper.cpp @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +using namespace cubos::core::reflection; + +CUBOS_REFLECT_IMPL(WrapperTrait) +{ + return Type::create("cubos::core::ecs::WrapperTrait"); +}