Skip to content

Commit

Permalink
refactor(core): add FieldTrait::{View, ConstView}
Browse files Browse the repository at this point in the history
  • Loading branch information
RiscadoA committed Sep 28, 2023
1 parent f7fa833 commit 922896d
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 11 deletions.
188 changes: 181 additions & 7 deletions core/include/cubos/core/reflection/traits/fields.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion core/samples/reflection/traits/fields/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ int main()

/// [Accessing fields by name]
Person person{.age = 21, .weight = 68.4F};
*static_cast<float*>(fields.field("weight")->pointerTo(&person)) += 20.0F;
*static_cast<float*>(fields.view(&person).get(*fields.field("weight"))) += 20.0F;
CUBOS_INFO("New weight: {}", person.weight); // 88.4
}
/// [Accessing fields by name]
124 changes: 124 additions & 0 deletions core/src/cubos/core/reflection/traits/fields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Check warning on line 158 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L158

Added line #L158 was not covered by tests
{
return Iterator{*this, mTrait.mFirstField};

Check warning on line 160 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L160

Added line #L160 was not covered by tests
}

FieldsTrait::View::Iterator FieldsTrait::View::end() const

Check warning on line 163 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L163

Added line #L163 was not covered by tests
{
return Iterator{*this, nullptr};

Check warning on line 165 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L165

Added line #L165 was not covered by tests
}

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

Check warning on line 179 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L179

Added line #L179 was not covered by tests
{
return Iterator{*this, mTrait.mFirstField};

Check warning on line 181 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L181

Added line #L181 was not covered by tests
}

FieldsTrait::ConstView::Iterator FieldsTrait::ConstView::end() const

Check warning on line 184 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L184

Added line #L184 was not covered by tests
{
return Iterator{*this, nullptr};

Check warning on line 186 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L186

Added line #L186 was not covered by tests
}

FieldsTrait::View::Iterator::Iterator(const View& view, const Field* field)
: mInstance{view.mInstance}
, mField{field}

Check warning on line 191 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L189-L191

Added lines #L189 - L191 were not covered by tests
{
}

bool FieldsTrait::View::Iterator::operator==(const Iterator& other) const

Check warning on line 195 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L195

Added line #L195 was not covered by tests
{
return mInstance == other.mInstance && mField == other.mField;

Check warning on line 197 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L197

Added line #L197 was not covered by tests
}

bool FieldsTrait::View::Iterator::operator!=(const Iterator& other) const

Check warning on line 200 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L200

Added line #L200 was not covered by tests
{
return !(*this == other);

Check warning on line 202 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L202

Added line #L202 was not covered by tests
}

const FieldsTrait::View::Iterator::Output& FieldsTrait::View::Iterator::operator*() const

Check warning on line 205 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L205

Added line #L205 was not covered by tests
{
CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds");
mOutput.field = mField;
mOutput.value = mField->pointerTo(mInstance);
return mOutput;

Check warning on line 210 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L207-L210

Added lines #L207 - L210 were not covered by tests
}

const FieldsTrait::View::Iterator::Output* FieldsTrait::View::Iterator::operator->() const

Check warning on line 213 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L213

Added line #L213 was not covered by tests
{
return &this->operator*();

Check warning on line 215 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L215

Added line #L215 was not covered by tests
}

FieldsTrait::View::Iterator& FieldsTrait::View::Iterator::operator++()

Check warning on line 218 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L218

Added line #L218 was not covered by tests
{
CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds");
mField = mField->next();
return *this;

Check warning on line 222 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L220-L222

Added lines #L220 - L222 were not covered by tests
}

FieldsTrait::ConstView::Iterator::Iterator(const ConstView& view, const Field* field)
: mInstance{view.mInstance}
, mField{field}

Check warning on line 227 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L225-L227

Added lines #L225 - L227 were not covered by tests
{
}

bool FieldsTrait::ConstView::Iterator::operator==(const Iterator& other) const

Check warning on line 231 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L231

Added line #L231 was not covered by tests
{
return mInstance == other.mInstance && mField == other.mField;

Check warning on line 233 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L233

Added line #L233 was not covered by tests
}

bool FieldsTrait::ConstView::Iterator::operator!=(const Iterator& other) const

Check warning on line 236 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L236

Added line #L236 was not covered by tests
{
return !(*this == other);

Check warning on line 238 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L238

Added line #L238 was not covered by tests
}

const FieldsTrait::ConstView::Iterator::Output& FieldsTrait::ConstView::Iterator::operator*() const

Check warning on line 241 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L241

Added line #L241 was not covered by tests
{
CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds");
mOutput.field = mField;
mOutput.value = mField->pointerTo(mInstance);
return mOutput;

Check warning on line 246 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L243-L246

Added lines #L243 - L246 were not covered by tests
}

const FieldsTrait::ConstView::Iterator::Output* FieldsTrait::ConstView::Iterator::operator->() const

Check warning on line 249 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L249

Added line #L249 was not covered by tests
{
return &this->operator*();

Check warning on line 251 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L251

Added line #L251 was not covered by tests
}

FieldsTrait::ConstView::Iterator& FieldsTrait::ConstView::Iterator::operator++()

Check warning on line 254 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L254

Added line #L254 was not covered by tests
{
CUBOS_ASSERT(mField != nullptr, "Iterator out of bounds");
mField = mField->next();
return *this;

Check warning on line 258 in core/src/cubos/core/reflection/traits/fields.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/cubos/core/reflection/traits/fields.cpp#L256-L258

Added lines #L256 - L258 were not covered by tests
}
7 changes: 4 additions & 3 deletions core/tests/reflection/traits/fields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SimpleType>());
CHECK(field->pointerTo(&object) == &object.foo);
CHECK(fields.view(&object).get(*field) == &object.foo);
}

SUBCASE("two fields")
Expand All @@ -61,9 +62,9 @@ TEST_CASE("reflection::FieldsTrait")
ObjectType object{};
CHECK(fooField->name() == "foo");
CHECK(fooField->type().is<SimpleType>());
CHECK(fooField->pointerTo(&object) == &object.foo);
CHECK(fields.view(&object).get(*fooField) == &object.foo);
CHECK(barField->name() == "bar");
CHECK(barField->type().is<SimpleType>());
CHECK(barField->pointerTo(static_cast<const ObjectType*>(&object)) == &object.bar);
CHECK(fields.view(static_cast<const ObjectType*>(&object)).get(*barField) == &object.bar);
}
}

0 comments on commit 922896d

Please sign in to comment.