From 922896d1e53d001125ec31bf5bd1082ea20090eb Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Thu, 28 Sep 2023 12:54:41 +0100 Subject: [PATCH] refactor(core): add FieldTrait::{View, ConstView} --- .../cubos/core/reflection/traits/fields.hpp | 188 +++++++++++++++++- .../samples/reflection/traits/fields/main.cpp | 2 +- .../cubos/core/reflection/traits/fields.cpp | 124 ++++++++++++ core/tests/reflection/traits/fields.cpp | 7 +- 4 files changed, 310 insertions(+), 11 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/fields.hpp b/core/include/cubos/core/reflection/traits/fields.hpp index 8dc9f1c6e..f9273db05 100644 --- a/core/include/cubos/core/reflection/traits/fields.hpp +++ b/core/include/cubos/core/reflection/traits/fields.hpp @@ -34,6 +34,12 @@ namespace cubos::core::reflection /// @brief Used to iterate over the fields on a fields trait. class Iterator; + /// @brief Provides mutable access to an object. + class View; + + /// @brief Provides immutable access to an object. + class ConstView; + ~FieldsTrait(); /// @brief Constructs. @@ -76,6 +82,14 @@ namespace cubos::core::reflection /// @return Iterator. static Iterator end(); + /// @brief Returns a view of the given object instance. + /// @param instance Object instance. + /// @return Object view. + View view(void* instance) const; + + /// @copydoc view(void*) const + ConstView view(const void* instance) const; + private: Field* mFirstField{nullptr}; Field* mLastField{nullptr}; @@ -132,6 +146,15 @@ namespace cubos::core::reflection /// @return Name of the field. const std::string& name() const; + /// @brief Returns the next field in the linked list. + /// @return Pointer to next field or null if this is the last field. + const Field* next() const; + + private: + friend FieldsTrait; + friend View; + friend ConstView; + /// @brief Returns the address of the field on a given instance. /// @param instance Pointer to the instance. /// @return Address of the field on the given instance. @@ -145,13 +168,6 @@ namespace cubos::core::reflection /// @copydoc pointerTo(void*) const void* pointerTo(const void* instance) const; - /// @brief Returns the next field in the linked list. - /// @return Pointer to next field or null if this is the last field. - const Field* next() const; - - private: - friend FieldsTrait; - const Type& mType; std::string mName; AddressOf* mAddressOf; @@ -191,4 +207,162 @@ namespace cubos::core::reflection private: const Field* mField; }; + + class FieldsTrait::View + { + public: + /// @brief Used to iterate over the fields of an object. + class Iterator; + + /// @brief Constructs. + /// @param trait Trait. + /// @param instance Instance. + View(const FieldsTrait& trait, void* instance); + + /// @brief Gets a pointer to the value of the given field on the object. + /// @param field Field. + /// @return Field value. + void* get(const Field& field) const; + + /// @brief Gets an iterator to the first field. + /// @return Iterator. + Iterator begin() const; + + /// @brief Gets an iterator to the field after the last field. + /// @return Iterator. + Iterator end() const; + + private: + const FieldsTrait& mTrait; + void* mInstance; + }; + + class FieldsTrait::ConstView + { + public: + /// @brief Used to iterate over the fields of an object. + class Iterator; + + /// @brief Constructs. + /// @param trait Trait. + /// @param instance Instance. + ConstView(const FieldsTrait& trait, const void* instance); + + /// @brief Gets a pointer to the value of the given field on the object. + /// @param field Field. + /// @return Field value. + const void* get(const Field& field) const; + + /// @brief Gets an iterator to the first field. + /// @return Iterator. + Iterator begin() const; + + /// @brief Gets an iterator to the field after the last field. + /// @return Iterator. + Iterator end() const; + + private: + const FieldsTrait& mTrait; + const void* mInstance; + }; + + class FieldsTrait::View::Iterator + { + public: + /// @brief Output structure for the iterator. + struct Output + { + const Field* field; ///< Field. + void* value; ///< Value. + }; + + /// @brief Constructs. + /// @param view View. + /// @param field Field. + Iterator(const View& view, const Field* field); + + /// @brief Copy constructs. + /// @param other Other iterator. + Iterator(const Iterator& other) = default; + + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to the same field. + bool operator==(const Iterator& other) const; + + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to different fields. + bool operator!=(const Iterator& /*other*/) const; + + /// @brief Accesses the field referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Output. + const Output& operator*() const; + + /// @brief Accesses the field referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Output. + const Output* operator->() const; + + /// @brief Advances the iterator. + /// @note Aborts if out of bounds. + /// @return Reference to this. + Iterator& operator++(); + + private: + void* mInstance; + const Field* mField; + mutable Output mOutput; + }; + + class FieldsTrait::ConstView::Iterator + { + public: + /// @brief Output structure for the iterator. + struct Output + { + const Field* field; ///< Field. + const void* value; ///< Value. + }; + + /// @brief Constructs. + /// @param view View. + /// @param field Field. + Iterator(const ConstView& view, const Field* field); + + /// @brief Copy constructs. + /// @param other Other iterator. + Iterator(const Iterator& other) = default; + + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to the same field. + bool operator==(const Iterator& other) const; + + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to different fields. + bool operator!=(const Iterator& /*other*/) const; + + /// @brief Accesses the field referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Output. + const Output& operator*() const; + + /// @brief Accesses the field referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Output. + const Output* operator->() const; + + /// @brief Advances the iterator. + /// @note Aborts if out of bounds. + /// @return Reference to this. + Iterator& operator++(); + + private: + const void* mInstance; + const Field* mField; + mutable Output mOutput; + }; } // namespace cubos::core::reflection diff --git a/core/samples/reflection/traits/fields/main.cpp b/core/samples/reflection/traits/fields/main.cpp index 0c603a1cb..7bec7efcc 100644 --- a/core/samples/reflection/traits/fields/main.cpp +++ b/core/samples/reflection/traits/fields/main.cpp @@ -48,7 +48,7 @@ int main() /// [Accessing fields by name] Person person{.age = 21, .weight = 68.4F}; - *static_cast(fields.field("weight")->pointerTo(&person)) += 20.0F; + *static_cast(fields.view(&person).get(*fields.field("weight"))) += 20.0F; CUBOS_INFO("New weight: {}", person.weight); // 88.4 } /// [Accessing fields by name] diff --git a/core/src/cubos/core/reflection/traits/fields.cpp b/core/src/cubos/core/reflection/traits/fields.cpp index 4790126d8..1c19f17b5 100644 --- a/core/src/cubos/core/reflection/traits/fields.cpp +++ b/core/src/cubos/core/reflection/traits/fields.cpp @@ -133,3 +133,127 @@ FieldsTrait::Iterator FieldsTrait::end() { return Iterator{nullptr}; } + +FieldsTrait::View FieldsTrait::view(void* instance) const +{ + return View{*this, instance}; +} + +FieldsTrait::ConstView FieldsTrait::view(const void* instance) const +{ + return ConstView{*this, instance}; +} + +FieldsTrait::View::View(const FieldsTrait& trait, void* instance) + : mTrait{trait} + , mInstance{instance} +{ +} + +void* FieldsTrait::View::get(const Field& field) const +{ + return field.pointerTo(mInstance); +} + +FieldsTrait::View::Iterator FieldsTrait::View::begin() const +{ + return Iterator{*this, mTrait.mFirstField}; +} + +FieldsTrait::View::Iterator FieldsTrait::View::end() const +{ + return Iterator{*this, nullptr}; +} + +FieldsTrait::ConstView::ConstView(const FieldsTrait& trait, const void* instance) + : mTrait{trait} + , mInstance{instance} +{ +} + +const void* FieldsTrait::ConstView::get(const Field& field) const +{ + return field.pointerTo(mInstance); +} + +FieldsTrait::ConstView::Iterator FieldsTrait::ConstView::begin() const +{ + return Iterator{*this, mTrait.mFirstField}; +} + +FieldsTrait::ConstView::Iterator FieldsTrait::ConstView::end() const +{ + return Iterator{*this, nullptr}; +} + +FieldsTrait::View::Iterator::Iterator(const View& view, const Field* field) + : mInstance{view.mInstance} + , mField{field} +{ +} + +bool FieldsTrait::View::Iterator::operator==(const Iterator& other) const +{ + return mInstance == other.mInstance && mField == other.mField; +} + +bool FieldsTrait::View::Iterator::operator!=(const Iterator& other) const +{ + return !(*this == other); +} + +const FieldsTrait::View::Iterator::Output& FieldsTrait::View::Iterator::operator*() const +{ + CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds"); + mOutput.field = mField; + mOutput.value = mField->pointerTo(mInstance); + return mOutput; +} + +const FieldsTrait::View::Iterator::Output* FieldsTrait::View::Iterator::operator->() const +{ + return &this->operator*(); +} + +FieldsTrait::View::Iterator& FieldsTrait::View::Iterator::operator++() +{ + CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds"); + mField = mField->next(); + return *this; +} + +FieldsTrait::ConstView::Iterator::Iterator(const ConstView& view, const Field* field) + : mInstance{view.mInstance} + , mField{field} +{ +} + +bool FieldsTrait::ConstView::Iterator::operator==(const Iterator& other) const +{ + return mInstance == other.mInstance && mField == other.mField; +} + +bool FieldsTrait::ConstView::Iterator::operator!=(const Iterator& other) const +{ + return !(*this == other); +} + +const FieldsTrait::ConstView::Iterator::Output& FieldsTrait::ConstView::Iterator::operator*() const +{ + CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds"); + mOutput.field = mField; + mOutput.value = mField->pointerTo(mInstance); + return mOutput; +} + +const FieldsTrait::ConstView::Iterator::Output* FieldsTrait::ConstView::Iterator::operator->() const +{ + return &this->operator*(); +} + +FieldsTrait::ConstView::Iterator& FieldsTrait::ConstView::Iterator::operator++() +{ + CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds"); + mField = mField->next(); + return *this; +} diff --git a/core/tests/reflection/traits/fields.cpp b/core/tests/reflection/traits/fields.cpp index 08cf512a8..b33c84c57 100644 --- a/core/tests/reflection/traits/fields.cpp +++ b/core/tests/reflection/traits/fields.cpp @@ -37,12 +37,13 @@ TEST_CASE("reflection::FieldsTrait") const auto* field = fields.field("foo"); REQUIRE(field != nullptr); CHECK(field == &*fields.begin()); + CHECK(fields.begin() != fields.end()); CHECK(++fields.begin() == fields.end()); ObjectType object{}; CHECK(field->name() == "foo"); CHECK(field->type().is()); - CHECK(field->pointerTo(&object) == &object.foo); + CHECK(fields.view(&object).get(*field) == &object.foo); } SUBCASE("two fields") @@ -61,9 +62,9 @@ TEST_CASE("reflection::FieldsTrait") ObjectType object{}; CHECK(fooField->name() == "foo"); CHECK(fooField->type().is()); - CHECK(fooField->pointerTo(&object) == &object.foo); + CHECK(fields.view(&object).get(*fooField) == &object.foo); CHECK(barField->name() == "bar"); CHECK(barField->type().is()); - CHECK(barField->pointerTo(static_cast(&object)) == &object.bar); + CHECK(fields.view(static_cast(&object)).get(*barField) == &object.bar); } }