From c61a2000d138edc87429bf389d5fd005701e3b72 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Thu, 21 Sep 2023 08:55:09 +0100 Subject: [PATCH 01/12] feat(core): add DictionaryTrait --- core/CMakeLists.txt | 1 + .../core/reflection/traits/dictionary.hpp | 307 ++++++++++++++++++ .../core/reflection/traits/dictionary.cpp | 293 +++++++++++++++++ 3 files changed, 601 insertions(+) create mode 100644 core/include/cubos/core/reflection/traits/dictionary.hpp create mode 100644 core/src/cubos/core/reflection/traits/dictionary.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f6fa7830a0..ff8ff0110c 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -35,6 +35,7 @@ set(CUBOS_CORE_SOURCE "src/cubos/core/reflection/traits/constructible.cpp" "src/cubos/core/reflection/traits/fields.cpp" "src/cubos/core/reflection/traits/array.cpp" + "src/cubos/core/reflection/traits/dictionary.cpp" "src/cubos/core/reflection/external/primitives.cpp" "src/cubos/core/data/serializer.cpp" diff --git a/core/include/cubos/core/reflection/traits/dictionary.hpp b/core/include/cubos/core/reflection/traits/dictionary.hpp new file mode 100644 index 0000000000..3a793dd921 --- /dev/null +++ b/core/include/cubos/core/reflection/traits/dictionary.hpp @@ -0,0 +1,307 @@ +/// @file +/// @brief Class @ref cubos::core::reflection::DictionaryTrait. +/// @ingroup core-reflection + +#pragma once + +#include +#include + +#include + +namespace cubos::core::reflection +{ + /// @brief Exposes dictionary-like functionality of a type. + /// @see See @ref examples-core-reflection-traits-dictionary for an example of using this trait. + /// @ingroup core-reflection + class DictionaryTrait final + { + public: + /// @brief Points to a key-value pair in a dictionary, allowing modification of the value. + class Iterator; + + /// @brief Points to a key-value pair in a dictionary. + class ConstIterator; + + /// @brief Function pointer to get the length of a dictionary instance. + /// @param instance Dictionary instance. + /// @return Length. + using Length = std::size_t (*)(const void* instance); + + /// @brief Function pointer to get an iterator to the first key-value pair of a dictionary + /// instance. + /// @note @ref Stop should be called on the returned iterator when it is no longer needed. + /// @param instance Dictionary instance. + /// @param writeable Whether the iterator should provide write access. + /// @return Iterator. + using Begin = void* (*)(uintptr_t instance, bool writeable); + + /// @brief Function pointer to get an iterator to a value in an dictionary instance. + /// @note @ref Stop should be called on the returned iterator when it is no longer needed. + /// @param instance Dictionary instance. + /// @param key Key. + /// @param writeable Whether the iterator should provide write access. + /// @return Iterator to the found key-value pair, or null if not found. + using Find = void* (*)(uintptr_t instance, const void* key, bool writeable); + + /// @brief Function pointer to advance an iterator. + /// @param instance Dictionary instance. + /// @param iterator Iterator. + /// @param writeable Whether the iterator provides write access. + /// @return Whether the iterator is still valid. + using Advance = bool (*)(uintptr_t instance, void* iterator, bool writeable); + + /// @brief Function pointer to destroy an iterator instance. + /// @param iterator Iterator to the key-value pair. + /// @param writeable Whether the iterator provides write access. + using Stop = void (*)(void* iterator, bool writeable); + + /// @brief Function pointer to get the address of the key pointed to by an iterator. + /// @param iterator Iterator. + /// @param writeable Whether the iterator provides write access. + /// @return Key address. + using Key = const void* (*)(const void* iterator, bool writeable); + + /// @brief Function pointer to get the address of the value pointed to by an iterator. + /// @param iterator Iterator. + /// @param writeable Whether the iterator provides write access. + /// @return Value address. + using Value = uintptr_t (*)(const void* iterator, bool writeable); + + /// @brief Function pointer to insert a default value into a dictionary instance. + /// @param instance Dictionary instance. + /// @param key Key. + using InsertDefault = void (*)(void* instance, const void* key); + + /// @brief Function pointer to insert a copy of the given value into a dictionary instance. + /// @param instance Dictionary instance. + /// @param key Key. + /// @param value Value. + using InsertCopy = void (*)(void* instance, const void* key, const void* value); + + /// @brief Function pointer to move the given value into a dictionary instance. + /// @param instance Dictionary instance. + /// @param key Key. + /// @param value Value. + using InsertMove = void (*)(void* instance, const void* key, void* value); + + /// @brief Function pointer to remove a key-value pair of a dictionary instance. + /// @param instance Dictionary instance. + /// @param iterator Iterator to the key-value pair. + /// @param writeable Whether the iterator provides write access. + using Erase = void (*)(void* instance, const void* iterator, bool writeable); + + /// @brief Constructs. + /// @param keyType Key type of the dictionary. + /// @param valueType Value type of the dictionary. + /// @param length Function used to get the length of a dictionary. + /// @param begin Function used to get an iterator to the first key-value pair of a + /// @param find Function used to find a key-value pair in a dictionary. + /// @param advance Function used to advance an iterator. + /// @param stop Function used to destroy an iterator. + /// @param key Function used to get the address of the key pointed to by an iterator. + /// @param value Function used to get the address of the value pointed to by an iterator. + DictionaryTrait(const Type& keyType, const Type& valueType, Length length, Begin begin, Find find, + Advance advance, Stop stop, Key key, Value value); + + /// @brief Sets the default-construct insert operation of the trait. + /// @param insertDefault Function pointer. + void setInsertDefault(InsertDefault insertDefault); + + /// @brief Sets the copy-construct insert operation of the trait. + /// @param insertCopy Function pointer. + void setInsertCopy(InsertCopy insertCopy); + + /// @brief Sets the move-construct insert operation of the trait. + /// @param insertMove Function pointer. + void setInsertMove(InsertMove insertMove); + + /// @brief Sets the erase operation of the trait. + /// @param erase Function pointer. + void setErase(Erase erase); + + /// @brief Returns the key type of the dictionary. + /// @return Key type. + const Type& keyType() const; + + /// @brief Returns the value type of the dictionary. + /// @return Value type. + const Type& valueType() const; + + /// @brief Returns the length of the given dictionary. + /// @param instance Dictionary instance. + /// @return Dictionary length. + std::size_t length(const void* instance) const; + + /// @brief Returns an iterator to the beginning of the given dictionary. + /// @param instance Dictionary instance. + /// @return Iterator. + Iterator begin(void* instance) const; + + /// @copydoc begin(void*) const + ConstIterator begin(const void* instance) const; + + /// @brief Returns an iterator to the element of the given dictionary with the given key. + /// @param instance Dictionary instance. + /// @param key Key. + /// @return Iterator to the found key-value pair, or null if not found. + Iterator find(void* instance, const void* key) const; + + /// @copydoc find(void*, const void*) const + ConstIterator find(const void* instance, const void* key) const; + + /// @brief Inserts a default-constructed value into the dicitonary. + /// @param instance Dictionary instance. + /// @param key Key. + /// @return Whether the operation is supported. + bool insertDefault(void* instance, const void* key) const; + + /// @brief Inserts a copy-constructed value into the dictionary. + /// @param instance Dictionary instance. + /// @param key Key. + /// @param value Value. + /// @return Whether the operation is supported. + bool insertCopy(void* instance, const void* key, const void* value) const; + + /// @brief Inserts a move-constructed value into the dictionary. + /// @param instance Dictionary instance. + /// @param key Key. + /// @param value Value. + /// @return Whether the operation is supported. + bool insertMove(void* instance, const void* key, void* value) const; + + /// @brief Removes a key-value pair of the dictionary. + /// @param instance Dictionary instance. + /// @param key Iterator. + /// @return Whether the operation is supported. + bool erase(void* instance, Iterator& iterator) const; + + /// @copydoc erase(void*, Iterator&) const + bool erase(void* instance, ConstIterator& iterator) const; + + /// @brief Checks if default-construct insert is supported. + /// @return Whether the operation is supported. + bool hasInsertDefault() const; + + /// @brief Checks if copy-construct insert is supported. + /// @return Whether the operation is supported. + bool hasInsertCopy() const; + + /// @brief Checks if move-construct insert is supported. + /// @return Whether the operation is supported. + bool hasInsertMove() const; + + /// @brief Checks if erase is supported. + /// @return Whether the operation is supported. + bool hasErase() const; + + private: + friend Iterator; + + const Type& mKeyType; + const Type& mValueType; + Length mLength; + Begin mBegin; + Find mFind; + Advance mAdvance; + Stop mStop; + Key mKey; + Value mValue; + InsertDefault mInsertDefault; + InsertCopy mInsertCopy; + InsertMove mInsertMove; + Erase mErase; + }; + + class DictionaryTrait::Iterator + { + public: + ~Iterator(); + + /// @brief Copy constructs. + /// @param other Other iterator. + Iterator(const Iterator& other); + + /// @brief Move constructs. + /// @param other Other iterator. + Iterator(Iterator&& other); + + /// @brief Gets a pointer to the current key. + /// @note Aborts if @ref isNull() returns true. + /// @return Key. + const void* key() const; + + /// @brief Gets a pointer to the current value. + /// @note Aborts if @ref isNull() returns true. + /// @return Value. + void* value() const; + + /// @brief Advances the iterator. + /// @note Aborts if @ref isNull() returns true. + /// @return Whether the iterator is still valid. + bool advance(); + + /// @brief Checks if the iterator is null. + /// @return Whether the iterator is null. + bool isNull() const; + + private: + friend DictionaryTrait; + + /// @brief Constructs. + /// @param inner Inner iterator. + /// @param instance Dictionary instance. + /// @param trait Dictionary trait. + Iterator(void* inner, void* instance, const DictionaryTrait& trait); + + void* mInner; + void* mInstance; + const DictionaryTrait& mTrait; + }; + + class DictionaryTrait::ConstIterator + { + public: + ~ConstIterator(); + + /// @brief Copy constructs. + /// @param other Other iterator. + ConstIterator(const ConstIterator& other); + + /// @brief Move constructs. + /// @param other Other iterator. + ConstIterator(ConstIterator&& other); + + /// @brief Gets a pointer to the current key. + /// @note Aborts if @ref isNull() returns true. + /// @return Key. + const void* key() const; + + /// @brief Gets a pointer to the current value. + /// @note Aborts if @ref isNull() returns true. + /// @return Value. + const void* value() const; + + /// @brief Advances the iterator. + /// @note Aborts if @ref isNull() returns true. + /// @return Whether the iterator is still valid. + bool advance(); + + /// @brief Checks if the iterator is null. + /// @return Whether the iterator is null. + bool isNull() const; + + private: + friend DictionaryTrait; + + /// @brief Constructs. + /// @param inner Inner iterator. + /// @param instance Dictionary instance. + /// @param trait Dictionary trait. + ConstIterator(void* inner, const void* instance, const DictionaryTrait& trait); + + void* mInner; + const void* mInstance; + const DictionaryTrait& mTrait; + }; +} // namespace cubos::core::reflection diff --git a/core/src/cubos/core/reflection/traits/dictionary.cpp b/core/src/cubos/core/reflection/traits/dictionary.cpp new file mode 100644 index 0000000000..f07ff986b4 --- /dev/null +++ b/core/src/cubos/core/reflection/traits/dictionary.cpp @@ -0,0 +1,293 @@ +#include +#include + +using cubos::core::reflection::DictionaryTrait; +using cubos::core::reflection::Type; + +DictionaryTrait::DictionaryTrait(const Type& keyType, const Type& valueType, Length length, Begin begin, Find find, + Advance advance, Stop stop, Key key, Value value) + : mKeyType(keyType) + , mValueType(valueType) + , mLength(length) + , mBegin(begin) + , mFind(find) + , mAdvance(advance) + , mStop(stop) + , mKey(key) + , mValue(value) + , mInsertDefault(nullptr) + , mInsertCopy(nullptr) + , mInsertMove(nullptr) + , mErase(nullptr) +{ +} + +void DictionaryTrait::setInsertDefault(InsertDefault insertDefault) +{ + CUBOS_ASSERT(!mInsertDefault, "Insert default already set"); + mInsertDefault = insertDefault; +} + +void DictionaryTrait::setInsertCopy(InsertCopy insertCopy) +{ + CUBOS_ASSERT(!mInsertCopy, "Insert copy already set"); + mInsertCopy = insertCopy; +} + +void DictionaryTrait::setInsertMove(InsertMove insertMove) +{ + CUBOS_ASSERT(!mInsertMove, "Insert move already set"); + mInsertMove = insertMove; +} + +void DictionaryTrait::setErase(Erase erase) +{ + CUBOS_ASSERT(!mErase, "Erase already set"); + mErase = erase; +} + +const Type& DictionaryTrait::keyType() const +{ + return mKeyType; +} + +const Type& DictionaryTrait::valueType() const +{ + return mValueType; +} + +std::size_t DictionaryTrait::length(const void* instance) const +{ + return mLength(instance); +} + +DictionaryTrait::Iterator DictionaryTrait::begin(void* instance) const +{ + return Iterator{mBegin(reinterpret_cast(instance), true), instance, *this}; +} + +DictionaryTrait::ConstIterator DictionaryTrait::begin(const void* instance) const +{ + return ConstIterator{mBegin(reinterpret_cast(instance), false), instance, *this}; +} + +DictionaryTrait::Iterator DictionaryTrait::find(void* instance, const void* key) const +{ + return Iterator{mFind(reinterpret_cast(instance), key, true), instance, *this}; +} + +DictionaryTrait::ConstIterator DictionaryTrait::find(const void* instance, const void* key) const +{ + return ConstIterator{mFind(reinterpret_cast(instance), key, false), instance, *this}; +} + +bool DictionaryTrait::insertDefault(void* instance, const void* key) const +{ + if (mInsertDefault) + { + mInsertDefault(instance, key); + return true; + } + + return false; +} + +bool DictionaryTrait::insertCopy(void* instance, const void* key, const void* value) const +{ + if (mInsertCopy) + { + mInsertCopy(instance, key, value); + return true; + } + + return false; +} + +bool DictionaryTrait::insertMove(void* instance, const void* key, void* value) const +{ + if (mInsertMove) + { + mInsertMove(instance, key, value); + return true; + } + + return false; +} + +bool DictionaryTrait::erase(void* instance, Iterator& iterator) const +{ + CUBOS_ASSERT(!iterator.isNull(), "Cannot erase null iterator"); + + if (mErase) + { + mErase(instance, iterator.mInner, true); + mStop(iterator.mInner, true); + iterator.mInner = nullptr; + return true; + } + + return false; +} + +bool DictionaryTrait::erase(void* instance, ConstIterator& iterator) const +{ + CUBOS_ASSERT(!iterator.isNull(), "Cannot erase null iterator"); + + if (mErase) + { + mErase(instance, iterator.mInner, false); + mStop(iterator.mInner, false); + iterator.mInner = nullptr; + return true; + } + + return false; +} + +bool DictionaryTrait::hasInsertDefault() const +{ + return mInsertDefault; +} + +bool DictionaryTrait::hasInsertCopy() const +{ + return mInsertCopy; +} + +bool DictionaryTrait::hasInsertMove() const +{ + return mInsertMove; +} + +bool DictionaryTrait::hasErase() const +{ + return mErase; +} + +DictionaryTrait::Iterator::~Iterator() +{ + if (mInner) + { + mTrait.mStop(mInner, true); + } +} + +DictionaryTrait::Iterator::Iterator(void* iterator, void* instance, const DictionaryTrait& trait) + : mInner(iterator) + , mInstance(instance) + , mTrait(trait) +{ +} + +DictionaryTrait::Iterator::Iterator(const Iterator& other) + : mInner(nullptr) + , mInstance(other.mInstance) + , mTrait(other.mTrait) +{ + if (!other.isNull()) + { + mInner = mTrait.mFind(reinterpret_cast(mInstance), other.key(), true); + } +} + +DictionaryTrait::Iterator::Iterator(Iterator&& other) + : mInner(other.mInner) + , mInstance(other.mInstance) + , mTrait(other.mTrait) +{ + other.mInner = nullptr; +} + +const void* DictionaryTrait::Iterator::key() const +{ + CUBOS_ASSERT(mInner != nullptr, "Cannot get key from null iterator"); + return mTrait.mKey(mInner, true); +} + +void* DictionaryTrait::Iterator::value() const +{ + CUBOS_ASSERT(mInner != nullptr, "Cannot get value from null iterator"); + return reinterpret_cast(mTrait.mValue(mInner, true)); +} + +bool DictionaryTrait::Iterator::advance() +{ + CUBOS_ASSERT(mInner != nullptr, "Cannot advance null iterator"); + + if (mTrait.mAdvance(reinterpret_cast(mInstance), mInner, true)) + { + return true; + } + + mInner = nullptr; + return false; +} + +bool DictionaryTrait::Iterator::isNull() const +{ + return mInner == nullptr; +} + +DictionaryTrait::ConstIterator::~ConstIterator() +{ + if (mInner) + { + mTrait.mStop(mInner, false); + } +} + +DictionaryTrait::ConstIterator::ConstIterator(void* iterator, const void* instance, const DictionaryTrait& trait) + : mInner(iterator) + , mInstance(instance) + , mTrait(trait) +{ +} + +DictionaryTrait::ConstIterator::ConstIterator(const ConstIterator& other) + : mInner(nullptr) + , mInstance(other.mInstance) + , mTrait(other.mTrait) +{ + if (!other.isNull()) + { + mInner = mTrait.mFind(reinterpret_cast(mInstance), other.key(), false); + } +} + +DictionaryTrait::ConstIterator::ConstIterator(ConstIterator&& other) + : mInner(other.mInner) + , mInstance(other.mInstance) + , mTrait(other.mTrait) +{ + other.mInner = nullptr; +} + +const void* DictionaryTrait::ConstIterator::key() const +{ + CUBOS_ASSERT(mInner != nullptr, "Cannot get key from null iterator"); + return mTrait.mKey(mInner, false); +} + +const void* DictionaryTrait::ConstIterator::value() const +{ + CUBOS_ASSERT(mInner != nullptr, "Cannot get value from null iterator"); + return reinterpret_cast(mTrait.mValue(mInner, false)); +} + +bool DictionaryTrait::ConstIterator::advance() +{ + CUBOS_ASSERT(mInner != nullptr, "Cannot advance null iterator"); + + if (mTrait.mAdvance(reinterpret_cast(mInstance), mInner, false)) + { + return true; + } + + mInner = nullptr; + return false; +} + +bool DictionaryTrait::ConstIterator::isNull() const +{ + return mInner == nullptr; +} \ No newline at end of file From efd4e77b36ddd01f6313218bccc6842f15316617 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:04:25 +0100 Subject: [PATCH 02/12] docs(core): add DictionaryTrait sample --- core/samples/CMakeLists.txt | 1 + core/samples/reflection/page.md | 1 + .../reflection/traits/dictionary/main.cpp | 66 +++++++++++++++++++ .../reflection/traits/dictionary/page.md | 39 +++++++++++ 4 files changed, 107 insertions(+) create mode 100644 core/samples/reflection/traits/dictionary/main.cpp create mode 100644 core/samples/reflection/traits/dictionary/page.md diff --git a/core/samples/CMakeLists.txt b/core/samples/CMakeLists.txt index d4461a27ee..fcb31042d8 100644 --- a/core/samples/CMakeLists.txt +++ b/core/samples/CMakeLists.txt @@ -31,6 +31,7 @@ make_sample(DIR "reflection/basic") make_sample(DIR "reflection/traits/constructible") make_sample(DIR "reflection/traits/fields") make_sample(DIR "reflection/traits/array") +make_sample(DIR "reflection/traits/dictionary") 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/page.md b/core/samples/reflection/page.md index 2fa17114ae..bb1808e921 100644 --- a/core/samples/reflection/page.md +++ b/core/samples/reflection/page.md @@ -6,3 +6,4 @@ - @subpage examples-core-reflection-traits-constructible - @copybrief examples-core-reflection-traits-constructible - @subpage examples-core-reflection-traits-fields - @copybrief examples-core-reflection-traits-fields - @subpage examples-core-reflection-traits-array - @copybrief examples-core-reflection-traits-array +- @subpage examples-core-reflection-traits-dictionary - @copybrief examples-core-reflection-traits-dictionary diff --git a/core/samples/reflection/traits/dictionary/main.cpp b/core/samples/reflection/traits/dictionary/main.cpp new file mode 100644 index 0000000000..d276a51ca6 --- /dev/null +++ b/core/samples/reflection/traits/dictionary/main.cpp @@ -0,0 +1,66 @@ +#include + +/// [Printing any dictionary] +#include +#include + +using cubos::core::reflection::DictionaryTrait; +using cubos::core::reflection::Type; + +void printDictionary(const Type& type, const void* instance) +{ + const auto& dictionaryTrait = type.get(); + /// [Printing any dictionary] + + /// [Getting dictionary length and types] + CUBOS_INFO("Dictionary with {} entries of key type {} and value type {}", dictionaryTrait.length(instance), + dictionaryTrait.keyType().name(), dictionaryTrait.valueType().name()); + /// [Getting dictionary length and types] + + /// [Getting dictionary entries] + if (!dictionaryTrait.keyType().is() || !dictionaryTrait.valueType().is()) + { + CUBOS_INFO("This function does not support printing dictionary with key and value types other than int"); + return; + } + + for (auto it = dictionaryTrait.begin(instance); !it.isNull(); it.advance()) + { + CUBOS_INFO("{} -> {}", *static_cast(it.key()), *static_cast(it.value())); + } +} +/// [Getting dictionary entries] + +/// [Typed wrapper] +template +void printDictionary(const T& dictionary) +{ + using cubos::core::reflection::reflect; + + printDictionary(reflect(), &dictionary); +} +/// [Typed wrapper] + +/// [Usage] +#include +#include + +int main() +{ + std::map map = { + {1, 2}, + {2, 4}, + {3, 6}, + {4, 8}, + }; + printDictionary(map); + + /// [Expected output] + // Dictionary with 4 entries of key type int32_t and value type int32_t + // 1 -> 2 + // 2 -> 4 + // 3 -> 6 + // 4 -> 8 + /// [Expected output] +} +/// [Usage] \ No newline at end of file diff --git a/core/samples/reflection/traits/dictionary/page.md b/core/samples/reflection/traits/dictionary/page.md new file mode 100644 index 0000000000..b7d9af7f52 --- /dev/null +++ b/core/samples/reflection/traits/dictionary/page.md @@ -0,0 +1,39 @@ +# Dictionary Trait {#examples-core-reflection-traits-dictionary} + +@brief Exposing and using dictionary functionality of a type. + +The @ref cubos::core::reflection::DictionaryTrait "DictionaryTrait" trait is +used to expose the dictionary functionality of a type. In this example, we will +write a function which takes a type and an instance of that type, and prints +its entries: + +@snippet reflection/traits/dictionary/main.cpp Printing any dictionary + +Through the trait, we can access the size of the dictionary and its key and +value types: + +@snippet reflection/traits/dictionary/main.cpp Getting dictionary length and types + +We can also iterate over the entries of a dictionary and access them: + +@snippet reflection/traits/dictionary/main.cpp Getting dictionary entries + +In this example, we're only supporting dictionaris which map `int32_t`s, but we +could for example implement a printing function which supports all primitive +types. + +To make calling our function easier, we can add a convenience typed wrapper: + +@snippet reflection/traits/dictionary/main.cpp Typed wrapper + +Using this function is now as simple as: + +@snippet reflection/traits/dictionary/main.cpp Usage + +Its important to note that both the includes above are necessary, as we're +reflecting the type `std::map`, which also means reflecting +`int32_t`. + +Executing the sample should output: + +@snippet reflection/traits/dictionary/main.cpp Expected output From a9fb9bc0f635dbc5a1e5598fb0d69d90dc6def35 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:05:47 +0100 Subject: [PATCH 03/12] feat(core): implement std::map reflection --- .../cubos/core/reflection/external/map.hpp | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 core/include/cubos/core/reflection/external/map.hpp diff --git a/core/include/cubos/core/reflection/external/map.hpp b/core/include/cubos/core/reflection/external/map.hpp new file mode 100644 index 0000000000..9d1f5ffcf2 --- /dev/null +++ b/core/include/cubos/core/reflection/external/map.hpp @@ -0,0 +1,132 @@ +/// @file +/// @brief Reflection declaration for `std::map`. +/// @ingroup core-reflection + +#pragma once + +#include + +#include +#include +#include + +CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) +{ + using Map = std::map; + + auto dictionaryTrait = DictionaryTrait( + reflect(), reflect(), + [](const void* instance) -> std::size_t { return static_cast(instance)->size(); } /*length*/, + [](uintptr_t instance, bool writeable) -> void* { + if (writeable) + { + return new Map::iterator(reinterpret_cast(instance)->begin()); + } + else + { + return new Map::const_iterator(reinterpret_cast(instance)->cbegin()); + } + } /*begin*/, + [](uintptr_t instance, const void* key, bool writeable) -> void* { + if (writeable) + { + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); + if (it == reinterpret_cast(instance)->end()) + { + return nullptr; + } + return new Map::iterator(it); + } + else + { + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); + if (it == reinterpret_cast(instance)->end()) + { + return nullptr; + } + return new Map::const_iterator(it); + } + } /*find*/, + [](uintptr_t instance, void* iterator, bool writeable) -> bool { + if (writeable) + { + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->end(); + } + else + { + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->cend(); + } + } /*advance*/, + [](void* iterator, bool writeable) { + if (writeable) + { + delete static_cast(iterator); + } + else + { + delete static_cast(iterator); + } + } /*stop*/, + [](const void* iterator, bool writeable) -> const void* { + if (writeable) + { + return &(*static_cast(iterator))->first; + } + else + { + return &(*static_cast(iterator))->first; + } + } /*key*/, + [](const void* iterator, bool writeable) -> uintptr_t { + if (writeable) + { + return reinterpret_cast(&(*static_cast(iterator))->second); + } + else + { + return reinterpret_cast(&(*static_cast(iterator))->second); + } + } /*value*/); + + // We supply the insert functions depending on the constructibility of the value type. + + if constexpr (std::is_default_constructible::value) + { + dictionaryTrait.setInsertDefault([](void* instance, const void* key) { + auto* map = static_cast(instance); + map->emplace(std::piecewise_construct, std::make_tuple(*static_cast(key)), std::make_tuple()); + }); + } + + if constexpr (std::is_copy_constructible::value) + { + dictionaryTrait.setInsertCopy([](void* instance, const void* key, const void* value) { + auto* map = static_cast(instance); + map->emplace(*static_cast(key), *static_cast(value)); + }); + } + + // Since V must be moveable for all std::map, we always supply this function. + dictionaryTrait.setInsertMove([](void* instance, const void* key, void* value) { + auto* map = static_cast(instance); + map->emplace(*static_cast(key), std::move(*static_cast(value))); + }); + + dictionaryTrait.setErase([](void* instance, const void* iterator, bool writeable) { + auto* map = static_cast(instance); + if (writeable) + { + map->erase(*static_cast(iterator)); + } + else + { + map->erase(*static_cast(iterator)); + } + }); + + return Type::create("std::map<" + reflect().name() + ", " + reflect().name() + ">") + .with(dictionaryTrait) + .with(autoConstructibleTrait>()); +} From 35699f5042684b1124764f7c9d1595d3b692e0e9 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:06:16 +0100 Subject: [PATCH 04/12] test(core): cover std::map reflection --- core/tests/CMakeLists.txt | 1 + core/tests/reflection/external/map.cpp | 25 +++++ core/tests/reflection/traits/dictionary.hpp | 110 ++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 core/tests/reflection/external/map.cpp create mode 100644 core/tests/reflection/traits/dictionary.hpp diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index a5bd316d06..0d08f059a3 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable( reflection/traits/fields.cpp reflection/external/primitives.cpp reflection/external/vector.cpp + reflection/external/map.cpp data/fs/embedded_archive.cpp data/fs/standard_archive.cpp diff --git a/core/tests/reflection/external/map.cpp b/core/tests/reflection/external/map.cpp new file mode 100644 index 0000000000..a4ee6a3c28 --- /dev/null +++ b/core/tests/reflection/external/map.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include + +#include "../traits/constructible.hpp" +#include "../traits/dictionary.hpp" + +template +static void test(const char* name, std::map map, K insertedKey, V insertedValue) +{ + CHECK(reflect>().name() == name); + testDictionary, K, V>(map, map.size(), &insertedKey, &insertedValue); + testConstructible>(&map); +} + +TEST_CASE("reflection::reflect>()") +{ + test("std::map", {{1, 2}, {2, 4}}, 3, 6); + test>("std::map>", + { + {'H', {{'i', true}, {'o', false}}}, + }, + 'A', {{'h', true}}); +} diff --git a/core/tests/reflection/traits/dictionary.hpp b/core/tests/reflection/traits/dictionary.hpp new file mode 100644 index 0000000000..a1467ceffc --- /dev/null +++ b/core/tests/reflection/traits/dictionary.hpp @@ -0,0 +1,110 @@ +#include + +#include + +#include +#include + +using cubos::core::reflection::DictionaryTrait; +using cubos::core::reflection::reflect; +using cubos::core::reflection::Type; + +/// @brief Checks if a type's DictionaryTrait works as expected. +/// @tparam T Type. +/// @tparam E Element type. +/// @param value Value to test. +/// @param length Initial length. +/// @param inserted Optional value to insert. +template +void testDictionary(T& value, std::size_t length, const K* insertedKey, V* insertedValue) +{ + (void)insertedValue; + + const Type& type = reflect(); + REQUIRE(type.has()); + const DictionaryTrait& trait = type.get(); + + REQUIRE(trait.length(&value) == length); + REQUIRE(trait.keyType().is()); + REQUIRE(trait.valueType().is()); + + // Check that both the normal and const iterators count the expected number of elements. + + std::size_t i = 0; + for (auto it = trait.begin(&value); !it.isNull(); it.advance()) + { + i += 1; + } + REQUIRE(i == length); + + i = 0; + for (auto it = trait.begin(static_cast(&value)); !it.isNull(); it.advance()) + { + i += 1; + } + REQUIRE(i == length); + + if (trait.hasInsertDefault()) + { + REQUIRE(trait.find(&value, insertedKey).isNull()); + REQUIRE(trait.insertDefault(&value, insertedKey)); + auto it = trait.find(&value, insertedKey); + REQUIRE(!it.isNull()); + + if constexpr (std::equality_comparable) + { + CHECK(*static_cast(it.key()) == *insertedKey); + } + + if constexpr (std::equality_comparable && std::default_initializable) + { + CHECK(*static_cast(it.value()) == V{}); + } + + REQUIRE(trait.length(&value) == length + 1); + REQUIRE(trait.erase(&value, it)); + REQUIRE(trait.length(&value) == length); + REQUIRE(trait.find(&value, insertedKey).isNull()); + } + + if (trait.hasInsertCopy()) + { + REQUIRE(trait.find(&value, insertedKey).isNull()); + REQUIRE(trait.insertCopy(&value, insertedKey, insertedValue)); + auto it = trait.find(&value, insertedKey); + REQUIRE(!it.isNull()); + + if constexpr (std::equality_comparable) + { + CHECK(*static_cast(it.key()) == *insertedKey); + } + + if constexpr (std::equality_comparable) + { + CHECK(*static_cast(it.value()) == *static_cast(insertedValue)); + } + + REQUIRE(trait.length(&value) == length + 1); + REQUIRE(trait.erase(&value, it)); + REQUIRE(trait.length(&value) == length); + REQUIRE(trait.find(&value, insertedKey).isNull()); + } + + if (trait.hasInsertMove()) + { + REQUIRE(trait.find(&value, insertedKey).isNull()); + REQUIRE(trait.insertMove(&value, insertedKey, insertedValue)); + auto it = trait.find(&value, insertedKey); + REQUIRE(!it.isNull()); + + if constexpr (std::equality_comparable) + { + CHECK(*static_cast(it.key()) == *insertedKey); + } + + REQUIRE(trait.length(&value) == length + 1); + REQUIRE(trait.erase(&value, it)); + REQUIRE(trait.length(&value) == length); + REQUIRE(trait.find(&value, insertedKey).isNull()); + } +} From e873db8ce857fa697a01461c830bc1509aa0fedd Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:08:01 +0100 Subject: [PATCH 05/12] feat(core): implement std::unordered_map reflection --- .../reflection/external/unordered_map.hpp | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 core/include/cubos/core/reflection/external/unordered_map.hpp diff --git a/core/include/cubos/core/reflection/external/unordered_map.hpp b/core/include/cubos/core/reflection/external/unordered_map.hpp new file mode 100644 index 0000000000..def429b802 --- /dev/null +++ b/core/include/cubos/core/reflection/external/unordered_map.hpp @@ -0,0 +1,132 @@ +/// @file +/// @brief Reflection declaration for `std::unordered_map`. +/// @ingroup core-reflection + +#pragma once + +#include + +#include +#include +#include + +CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map)) +{ + using Map = std::unordered_map; + + auto dictionaryTrait = DictionaryTrait( + reflect(), reflect(), + [](const void* instance) -> std::size_t { return static_cast(instance)->size(); } /*length*/, + [](uintptr_t instance, bool writeable) -> void* { + if (writeable) + { + return new Map::iterator(reinterpret_cast(instance)->begin()); + } + else + { + return new Map::const_iterator(reinterpret_cast(instance)->cbegin()); + } + } /*begin*/, + [](uintptr_t instance, const void* key, bool writeable) -> void* { + if (writeable) + { + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); + if (it == reinterpret_cast(instance)->end()) + { + return nullptr; + } + return new Map::iterator(it); + } + else + { + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); + if (it == reinterpret_cast(instance)->end()) + { + return nullptr; + } + return new Map::const_iterator(it); + } + } /*find*/, + [](uintptr_t instance, void* iterator, bool writeable) { + if (writeable) + { + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->end(); + } + else + { + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->cend(); + } + } /*advance*/, + [](void* iterator, bool writeable) { + if (writeable) + { + delete static_cast(iterator); + } + else + { + delete static_cast(iterator); + } + } /*stop*/, + [](const void* iterator, bool writeable) -> const void* { + if (writeable) + { + return &(*static_cast(iterator))->first; + } + else + { + return &(*static_cast(iterator))->first; + } + } /*key*/, + [](const void* iterator, bool writeable) -> uintptr_t { + if (writeable) + { + return reinterpret_cast(&(*static_cast(iterator))->second); + } + else + { + return reinterpret_cast(&(*static_cast(iterator))->second); + } + } /*value*/); + + // We supply the insert functions depending on the constructibility of the value type. + + if constexpr (std::is_default_constructible::value) + { + dictionaryTrait.setInsertDefault([](void* instance, const void* key) { + auto* map = static_cast(instance); + map->emplace(std::piecewise_construct, std::make_tuple(*static_cast(key)), std::make_tuple()); + }); + } + + if constexpr (std::is_copy_constructible::value) + { + dictionaryTrait.setInsertCopy([](void* instance, const void* key, const void* value) { + auto* map = static_cast(instance); + map->emplace(*static_cast(key), *static_cast(value)); + }); + } + + // Since V must be moveable for all std::map, we always supply this function. + dictionaryTrait.setInsertMove([](void* instance, const void* key, void* value) { + auto* map = static_cast(instance); + map->emplace(*static_cast(key), std::move(*static_cast(value))); + }); + + dictionaryTrait.setErase([](void* instance, const void* iterator, bool writeable) { + auto* map = static_cast(instance); + if (writeable) + { + map->erase(*static_cast(iterator)); + } + else + { + map->erase(*static_cast(iterator)); + } + }); + + return Type::create("std::unordered_map<" + reflect().name() + ", " + reflect().name() + ">") + .with(dictionaryTrait) + .with(autoConstructibleTrait>()); +} From d73e51497d72d9f6a5ca7582da4d3b75c983fd93 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:08:14 +0100 Subject: [PATCH 06/12] test(core): cover std::unordered_map reflection --- core/tests/CMakeLists.txt | 1 + .../reflection/external/unordered_map.cpp | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 core/tests/reflection/external/unordered_map.cpp diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index 0d08f059a3..c6ae33a0f5 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable( reflection/external/primitives.cpp reflection/external/vector.cpp reflection/external/map.cpp + reflection/external/unordered_map.cpp data/fs/embedded_archive.cpp data/fs/standard_archive.cpp diff --git a/core/tests/reflection/external/unordered_map.cpp b/core/tests/reflection/external/unordered_map.cpp new file mode 100644 index 0000000000..8b722aea5d --- /dev/null +++ b/core/tests/reflection/external/unordered_map.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include + +#include "../traits/constructible.hpp" +#include "../traits/dictionary.hpp" + +template +static void test(const char* name, std::unordered_map map, K insertedKey, V insertedValue) +{ + CHECK(reflect>().name() == name); + testDictionary, K, V>(map, map.size(), &insertedKey, &insertedValue); + testConstructible>(&map); +} + +TEST_CASE("reflection::reflect>()") +{ + test("std::unordered_map", {{1, 2}, {2, 4}}, 3, 6); + test>("std::unordered_map>", + { + {'H', {{'i', true}, {'o', false}}}, + }, + 'A', {{'h', true}}); +} From bdb0ef358b81a0c1504fd94b27082c1cee9af2a3 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:10:46 +0100 Subject: [PATCH 07/12] fix(core): follow clang-tidy suggestions --- .../cubos/core/reflection/external/map.hpp | 59 +++++++++---------- .../reflection/external/unordered_map.hpp | 59 +++++++++---------- .../core/reflection/traits/dictionary.hpp | 4 +- .../reflection/traits/dictionary/main.cpp | 2 +- .../core/reflection/traits/dictionary.cpp | 36 +++++------ 5 files changed, 75 insertions(+), 85 deletions(-) diff --git a/core/include/cubos/core/reflection/external/map.hpp b/core/include/cubos/core/reflection/external/map.hpp index 9d1f5ffcf2..1016c50192 100644 --- a/core/include/cubos/core/reflection/external/map.hpp +++ b/core/include/cubos/core/reflection/external/map.hpp @@ -20,12 +20,11 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) [](uintptr_t instance, bool writeable) -> void* { if (writeable) { - return new Map::iterator(reinterpret_cast(instance)->begin()); - } - else - { - return new Map::const_iterator(reinterpret_cast(instance)->cbegin()); + return new typename Map::iterator(reinterpret_cast(instance)->begin()); } + + return new typename Map::const_iterator(reinterpret_cast(instance)->cbegin()); + } /*begin*/, [](uintptr_t instance, const void* key, bool writeable) -> void* { if (writeable) @@ -35,59 +34,55 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) { return nullptr; } - return new Map::iterator(it); + return new typename Map::iterator(it); } - else - { - auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); + + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); if (it == reinterpret_cast(instance)->end()) { return nullptr; } - return new Map::const_iterator(it); - } + return new typename Map::const_iterator(it); + } /*find*/, [](uintptr_t instance, void* iterator, bool writeable) -> bool { if (writeable) { - ++*static_cast(iterator); - return *static_cast(iterator) != reinterpret_cast(instance)->end(); - } - else - { - ++*static_cast(iterator); - return *static_cast(iterator) != reinterpret_cast(instance)->cend(); + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->end(); } + + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->cend(); + } /*advance*/, [](void* iterator, bool writeable) { if (writeable) { - delete static_cast(iterator); + delete static_cast(iterator); } else { - delete static_cast(iterator); + delete static_cast(iterator); } } /*stop*/, [](const void* iterator, bool writeable) -> const void* { if (writeable) { - return &(*static_cast(iterator))->first; - } - else - { - return &(*static_cast(iterator))->first; + return &(*static_cast(iterator))->first; } + + return &(*static_cast(iterator))->first; + } /*key*/, [](const void* iterator, bool writeable) -> uintptr_t { if (writeable) { - return reinterpret_cast(&(*static_cast(iterator))->second); - } - else - { - return reinterpret_cast(&(*static_cast(iterator))->second); + return reinterpret_cast(&(*static_cast(iterator))->second); } + + return reinterpret_cast(&(*static_cast(iterator))->second); + } /*value*/); // We supply the insert functions depending on the constructibility of the value type. @@ -118,11 +113,11 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) auto* map = static_cast(instance); if (writeable) { - map->erase(*static_cast(iterator)); + map->erase(*static_cast(iterator)); } else { - map->erase(*static_cast(iterator)); + map->erase(*static_cast(iterator)); } }); diff --git a/core/include/cubos/core/reflection/external/unordered_map.hpp b/core/include/cubos/core/reflection/external/unordered_map.hpp index def429b802..44bc293623 100644 --- a/core/include/cubos/core/reflection/external/unordered_map.hpp +++ b/core/include/cubos/core/reflection/external/unordered_map.hpp @@ -20,12 +20,11 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map void* { if (writeable) { - return new Map::iterator(reinterpret_cast(instance)->begin()); - } - else - { - return new Map::const_iterator(reinterpret_cast(instance)->cbegin()); + return new typename Map::iterator(reinterpret_cast(instance)->begin()); } + + return new typename Map::const_iterator(reinterpret_cast(instance)->cbegin()); + } /*begin*/, [](uintptr_t instance, const void* key, bool writeable) -> void* { if (writeable) @@ -35,59 +34,55 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map(instance)->find(*reinterpret_cast(key)); + + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); if (it == reinterpret_cast(instance)->end()) { return nullptr; } - return new Map::const_iterator(it); - } + return new typename Map::const_iterator(it); + } /*find*/, [](uintptr_t instance, void* iterator, bool writeable) { if (writeable) { - ++*static_cast(iterator); - return *static_cast(iterator) != reinterpret_cast(instance)->end(); - } - else - { - ++*static_cast(iterator); - return *static_cast(iterator) != reinterpret_cast(instance)->cend(); + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->end(); } + + ++*static_cast(iterator); + return *static_cast(iterator) != reinterpret_cast(instance)->cend(); + } /*advance*/, [](void* iterator, bool writeable) { if (writeable) { - delete static_cast(iterator); + delete static_cast(iterator); } else { - delete static_cast(iterator); + delete static_cast(iterator); } } /*stop*/, [](const void* iterator, bool writeable) -> const void* { if (writeable) { - return &(*static_cast(iterator))->first; - } - else - { - return &(*static_cast(iterator))->first; + return &(*static_cast(iterator))->first; } + + return &(*static_cast(iterator))->first; + } /*key*/, [](const void* iterator, bool writeable) -> uintptr_t { if (writeable) { - return reinterpret_cast(&(*static_cast(iterator))->second); - } - else - { - return reinterpret_cast(&(*static_cast(iterator))->second); + return reinterpret_cast(&(*static_cast(iterator))->second); } + + return reinterpret_cast(&(*static_cast(iterator))->second); + } /*value*/); // We supply the insert functions depending on the constructibility of the value type. @@ -118,11 +113,11 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map(instance); if (writeable) { - map->erase(*static_cast(iterator)); + map->erase(*static_cast(iterator)); } else { - map->erase(*static_cast(iterator)); + map->erase(*static_cast(iterator)); } }); diff --git a/core/include/cubos/core/reflection/traits/dictionary.hpp b/core/include/cubos/core/reflection/traits/dictionary.hpp index 3a793dd921..cf6d3c9439 100644 --- a/core/include/cubos/core/reflection/traits/dictionary.hpp +++ b/core/include/cubos/core/reflection/traits/dictionary.hpp @@ -224,7 +224,7 @@ namespace cubos::core::reflection /// @brief Move constructs. /// @param other Other iterator. - Iterator(Iterator&& other); + Iterator(Iterator&& other) noexcept ; /// @brief Gets a pointer to the current key. /// @note Aborts if @ref isNull() returns true. @@ -270,7 +270,7 @@ namespace cubos::core::reflection /// @brief Move constructs. /// @param other Other iterator. - ConstIterator(ConstIterator&& other); + ConstIterator(ConstIterator&& other) noexcept ; /// @brief Gets a pointer to the current key. /// @note Aborts if @ref isNull() returns true. diff --git a/core/samples/reflection/traits/dictionary/main.cpp b/core/samples/reflection/traits/dictionary/main.cpp index d276a51ca6..218bbed768 100644 --- a/core/samples/reflection/traits/dictionary/main.cpp +++ b/core/samples/reflection/traits/dictionary/main.cpp @@ -63,4 +63,4 @@ int main() // 4 -> 8 /// [Expected output] } -/// [Usage] \ No newline at end of file +/// [Usage] diff --git a/core/src/cubos/core/reflection/traits/dictionary.cpp b/core/src/cubos/core/reflection/traits/dictionary.cpp index f07ff986b4..a1fd4f2000 100644 --- a/core/src/cubos/core/reflection/traits/dictionary.cpp +++ b/core/src/cubos/core/reflection/traits/dictionary.cpp @@ -83,7 +83,7 @@ DictionaryTrait::ConstIterator DictionaryTrait::find(const void* instance, const bool DictionaryTrait::insertDefault(void* instance, const void* key) const { - if (mInsertDefault) + if (mInsertDefault != nullptr) { mInsertDefault(instance, key); return true; @@ -94,7 +94,7 @@ bool DictionaryTrait::insertDefault(void* instance, const void* key) const bool DictionaryTrait::insertCopy(void* instance, const void* key, const void* value) const { - if (mInsertCopy) + if (mInsertCopy != nullptr) { mInsertCopy(instance, key, value); return true; @@ -105,7 +105,7 @@ bool DictionaryTrait::insertCopy(void* instance, const void* key, const void* va bool DictionaryTrait::insertMove(void* instance, const void* key, void* value) const { - if (mInsertMove) + if (mInsertMove != nullptr) { mInsertMove(instance, key, value); return true; @@ -118,7 +118,7 @@ bool DictionaryTrait::erase(void* instance, Iterator& iterator) const { CUBOS_ASSERT(!iterator.isNull(), "Cannot erase null iterator"); - if (mErase) + if (mErase != nullptr) { mErase(instance, iterator.mInner, true); mStop(iterator.mInner, true); @@ -133,7 +133,7 @@ bool DictionaryTrait::erase(void* instance, ConstIterator& iterator) const { CUBOS_ASSERT(!iterator.isNull(), "Cannot erase null iterator"); - if (mErase) + if (mErase != nullptr) { mErase(instance, iterator.mInner, false); mStop(iterator.mInner, false); @@ -146,34 +146,34 @@ bool DictionaryTrait::erase(void* instance, ConstIterator& iterator) const bool DictionaryTrait::hasInsertDefault() const { - return mInsertDefault; + return mInsertDefault != nullptr; } bool DictionaryTrait::hasInsertCopy() const { - return mInsertCopy; + return mInsertCopy != nullptr; } bool DictionaryTrait::hasInsertMove() const { - return mInsertMove; + return mInsertMove != nullptr; } bool DictionaryTrait::hasErase() const { - return mErase; + return mErase != nullptr; } DictionaryTrait::Iterator::~Iterator() { - if (mInner) + if (mInner != nullptr) { mTrait.mStop(mInner, true); } } -DictionaryTrait::Iterator::Iterator(void* iterator, void* instance, const DictionaryTrait& trait) - : mInner(iterator) +DictionaryTrait::Iterator::Iterator(void* inner, void* instance, const DictionaryTrait& trait) + : mInner(inner) , mInstance(instance) , mTrait(trait) { @@ -191,7 +191,7 @@ DictionaryTrait::Iterator::Iterator(const Iterator& other) } DictionaryTrait::Iterator::Iterator(Iterator&& other) - : mInner(other.mInner) + noexcept : mInner(other.mInner) , mInstance(other.mInstance) , mTrait(other.mTrait) { @@ -230,14 +230,14 @@ bool DictionaryTrait::Iterator::isNull() const DictionaryTrait::ConstIterator::~ConstIterator() { - if (mInner) + if (mInner != nullptr) { mTrait.mStop(mInner, false); } } -DictionaryTrait::ConstIterator::ConstIterator(void* iterator, const void* instance, const DictionaryTrait& trait) - : mInner(iterator) +DictionaryTrait::ConstIterator::ConstIterator(void* inner, const void* instance, const DictionaryTrait& trait) + : mInner(inner) , mInstance(instance) , mTrait(trait) { @@ -255,7 +255,7 @@ DictionaryTrait::ConstIterator::ConstIterator(const ConstIterator& other) } DictionaryTrait::ConstIterator::ConstIterator(ConstIterator&& other) - : mInner(other.mInner) + noexcept : mInner(other.mInner) , mInstance(other.mInstance) , mTrait(other.mTrait) { @@ -290,4 +290,4 @@ bool DictionaryTrait::ConstIterator::advance() bool DictionaryTrait::ConstIterator::isNull() const { return mInner == nullptr; -} \ No newline at end of file +} From 8797a54fad6e6993aa3846602a07c944a61e736a Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:38:00 +0100 Subject: [PATCH 08/12] fix(core): free dictionary iterator on end --- core/src/cubos/core/reflection/traits/dictionary.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/cubos/core/reflection/traits/dictionary.cpp b/core/src/cubos/core/reflection/traits/dictionary.cpp index a1fd4f2000..72d961bf16 100644 --- a/core/src/cubos/core/reflection/traits/dictionary.cpp +++ b/core/src/cubos/core/reflection/traits/dictionary.cpp @@ -219,6 +219,7 @@ bool DictionaryTrait::Iterator::advance() return true; } + mTrait.mStop(mInner, true); mInner = nullptr; return false; } @@ -283,6 +284,7 @@ bool DictionaryTrait::ConstIterator::advance() return true; } + mTrait.mStop(mInner, false); mInner = nullptr; return false; } From ebde7aab312ed1140f388c1a5fcc717b8b446b03 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:38:11 +0100 Subject: [PATCH 09/12] test(core): cover const dictionary find --- core/tests/reflection/traits/dictionary.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/reflection/traits/dictionary.hpp b/core/tests/reflection/traits/dictionary.hpp index a1467ceffc..afc507a81a 100644 --- a/core/tests/reflection/traits/dictionary.hpp +++ b/core/tests/reflection/traits/dictionary.hpp @@ -48,7 +48,7 @@ void testDictionary(T& value, std::size_t length, const K* insertedKey, V* inser { REQUIRE(trait.find(&value, insertedKey).isNull()); REQUIRE(trait.insertDefault(&value, insertedKey)); - auto it = trait.find(&value, insertedKey); + auto it = trait.find(static_cast(&value), insertedKey); REQUIRE(!it.isNull()); if constexpr (std::equality_comparable) @@ -94,7 +94,7 @@ void testDictionary(T& value, std::size_t length, const K* insertedKey, V* inser { REQUIRE(trait.find(&value, insertedKey).isNull()); REQUIRE(trait.insertMove(&value, insertedKey, insertedValue)); - auto it = trait.find(&value, insertedKey); + auto it = trait.find(static_cast(&value), insertedKey); REQUIRE(!it.isNull()); if constexpr (std::equality_comparable) From 337eed63ad9b0d7fbb28840859dae2153eed167d Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Tue, 26 Sep 2023 15:54:37 +0100 Subject: [PATCH 10/12] style(core): format code --- .../cubos/core/reflection/external/map.hpp | 38 +++++++++---------- .../reflection/external/unordered_map.hpp | 38 +++++++++---------- .../core/reflection/traits/dictionary.hpp | 12 +++--- .../core/reflection/traits/dictionary.cpp | 18 +++------ 4 files changed, 46 insertions(+), 60 deletions(-) diff --git a/core/include/cubos/core/reflection/external/map.hpp b/core/include/cubos/core/reflection/external/map.hpp index 1016c50192..ad2503a286 100644 --- a/core/include/cubos/core/reflection/external/map.hpp +++ b/core/include/cubos/core/reflection/external/map.hpp @@ -22,9 +22,8 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) { return new typename Map::iterator(reinterpret_cast(instance)->begin()); } - - return new typename Map::const_iterator(reinterpret_cast(instance)->cbegin()); - + + return new typename Map::const_iterator(reinterpret_cast(instance)->cbegin()); } /*begin*/, [](uintptr_t instance, const void* key, bool writeable) -> void* { if (writeable) @@ -36,14 +35,13 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) } return new typename Map::iterator(it); } - - auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); - if (it == reinterpret_cast(instance)->end()) - { - return nullptr; - } - return new typename Map::const_iterator(it); - + + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); + if (it == reinterpret_cast(instance)->end()) + { + return nullptr; + } + return new typename Map::const_iterator(it); } /*find*/, [](uintptr_t instance, void* iterator, bool writeable) -> bool { if (writeable) @@ -51,10 +49,10 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) ++*static_cast(iterator); return *static_cast(iterator) != reinterpret_cast(instance)->end(); } - - ++*static_cast(iterator); - return *static_cast(iterator) != reinterpret_cast(instance)->cend(); - + + ++*static_cast(iterator); + return *static_cast(iterator) != + reinterpret_cast(instance)->cend(); } /*advance*/, [](void* iterator, bool writeable) { if (writeable) @@ -71,18 +69,16 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) { return &(*static_cast(iterator))->first; } - - return &(*static_cast(iterator))->first; - + + return &(*static_cast(iterator))->first; } /*key*/, [](const void* iterator, bool writeable) -> uintptr_t { if (writeable) { return reinterpret_cast(&(*static_cast(iterator))->second); } - - return reinterpret_cast(&(*static_cast(iterator))->second); - + + return reinterpret_cast(&(*static_cast(iterator))->second); } /*value*/); // We supply the insert functions depending on the constructibility of the value type. diff --git a/core/include/cubos/core/reflection/external/unordered_map.hpp b/core/include/cubos/core/reflection/external/unordered_map.hpp index 44bc293623..39183367d8 100644 --- a/core/include/cubos/core/reflection/external/unordered_map.hpp +++ b/core/include/cubos/core/reflection/external/unordered_map.hpp @@ -22,9 +22,8 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map(instance)->begin()); } - - return new typename Map::const_iterator(reinterpret_cast(instance)->cbegin()); - + + return new typename Map::const_iterator(reinterpret_cast(instance)->cbegin()); } /*begin*/, [](uintptr_t instance, const void* key, bool writeable) -> void* { if (writeable) @@ -36,14 +35,13 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map(instance)->find(*reinterpret_cast(key)); - if (it == reinterpret_cast(instance)->end()) - { - return nullptr; - } - return new typename Map::const_iterator(it); - + + auto it = reinterpret_cast(instance)->find(*reinterpret_cast(key)); + if (it == reinterpret_cast(instance)->end()) + { + return nullptr; + } + return new typename Map::const_iterator(it); } /*find*/, [](uintptr_t instance, void* iterator, bool writeable) { if (writeable) @@ -51,10 +49,10 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map(iterator); return *static_cast(iterator) != reinterpret_cast(instance)->end(); } - - ++*static_cast(iterator); - return *static_cast(iterator) != reinterpret_cast(instance)->cend(); - + + ++*static_cast(iterator); + return *static_cast(iterator) != + reinterpret_cast(instance)->cend(); } /*advance*/, [](void* iterator, bool writeable) { if (writeable) @@ -71,18 +69,16 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map(iterator))->first; } - - return &(*static_cast(iterator))->first; - + + return &(*static_cast(iterator))->first; } /*key*/, [](const void* iterator, bool writeable) -> uintptr_t { if (writeable) { return reinterpret_cast(&(*static_cast(iterator))->second); } - - return reinterpret_cast(&(*static_cast(iterator))->second); - + + return reinterpret_cast(&(*static_cast(iterator))->second); } /*value*/); // We supply the insert functions depending on the constructibility of the value type. diff --git a/core/include/cubos/core/reflection/traits/dictionary.hpp b/core/include/cubos/core/reflection/traits/dictionary.hpp index cf6d3c9439..c48bf2739e 100644 --- a/core/include/cubos/core/reflection/traits/dictionary.hpp +++ b/core/include/cubos/core/reflection/traits/dictionary.hpp @@ -207,10 +207,10 @@ namespace cubos::core::reflection Stop mStop; Key mKey; Value mValue; - InsertDefault mInsertDefault; - InsertCopy mInsertCopy; - InsertMove mInsertMove; - Erase mErase; + InsertDefault mInsertDefault{nullptr}; + InsertCopy mInsertCopy{nullptr}; + InsertMove mInsertMove{nullptr}; + Erase mErase{nullptr}; }; class DictionaryTrait::Iterator @@ -254,7 +254,7 @@ namespace cubos::core::reflection /// @param trait Dictionary trait. Iterator(void* inner, void* instance, const DictionaryTrait& trait); - void* mInner; + void* mInner{nullptr}; void* mInstance; const DictionaryTrait& mTrait; }; @@ -300,7 +300,7 @@ namespace cubos::core::reflection /// @param trait Dictionary trait. ConstIterator(void* inner, const void* instance, const DictionaryTrait& trait); - void* mInner; + void* mInner{nullptr}; const void* mInstance; const DictionaryTrait& mTrait; }; diff --git a/core/src/cubos/core/reflection/traits/dictionary.cpp b/core/src/cubos/core/reflection/traits/dictionary.cpp index 72d961bf16..b265b3c484 100644 --- a/core/src/cubos/core/reflection/traits/dictionary.cpp +++ b/core/src/cubos/core/reflection/traits/dictionary.cpp @@ -15,10 +15,6 @@ DictionaryTrait::DictionaryTrait(const Type& keyType, const Type& valueType, Len , mStop(stop) , mKey(key) , mValue(value) - , mInsertDefault(nullptr) - , mInsertCopy(nullptr) - , mInsertMove(nullptr) - , mErase(nullptr) { } @@ -180,8 +176,7 @@ DictionaryTrait::Iterator::Iterator(void* inner, void* instance, const Dictionar } DictionaryTrait::Iterator::Iterator(const Iterator& other) - : mInner(nullptr) - , mInstance(other.mInstance) + : mInstance(other.mInstance) , mTrait(other.mTrait) { if (!other.isNull()) @@ -190,8 +185,8 @@ DictionaryTrait::Iterator::Iterator(const Iterator& other) } } -DictionaryTrait::Iterator::Iterator(Iterator&& other) - noexcept : mInner(other.mInner) +DictionaryTrait::Iterator::Iterator(Iterator&& other) noexcept + : mInner(other.mInner) , mInstance(other.mInstance) , mTrait(other.mTrait) { @@ -245,8 +240,7 @@ DictionaryTrait::ConstIterator::ConstIterator(void* inner, const void* instance, } DictionaryTrait::ConstIterator::ConstIterator(const ConstIterator& other) - : mInner(nullptr) - , mInstance(other.mInstance) + : mInstance(other.mInstance) , mTrait(other.mTrait) { if (!other.isNull()) @@ -255,8 +249,8 @@ DictionaryTrait::ConstIterator::ConstIterator(const ConstIterator& other) } } -DictionaryTrait::ConstIterator::ConstIterator(ConstIterator&& other) - noexcept : mInner(other.mInner) +DictionaryTrait::ConstIterator::ConstIterator(ConstIterator&& other) noexcept + : mInner(other.mInner) , mInstance(other.mInstance) , mTrait(other.mTrait) { From 00d4a2411de6cd326d6afb6166d2a4df895b9e02 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Thu, 28 Sep 2023 10:58:28 +0100 Subject: [PATCH 11/12] refactor(core): add DictionaryTrait::{View, ConstView} --- .../cubos/core/reflection/external/map.hpp | 12 +- .../reflection/external/unordered_map.hpp | 14 +- .../core/reflection/traits/dictionary.hpp | 295 +++++++++++------- .../reflection/traits/dictionary/main.cpp | 9 +- .../core/reflection/traits/dictionary.cpp | 264 ++++++++-------- core/tests/reflection/traits/dictionary.hpp | 71 +++-- 6 files changed, 369 insertions(+), 296 deletions(-) diff --git a/core/include/cubos/core/reflection/external/map.hpp b/core/include/cubos/core/reflection/external/map.hpp index ad2503a286..924e5c8f9a 100644 --- a/core/include/cubos/core/reflection/external/map.hpp +++ b/core/include/cubos/core/reflection/external/map.hpp @@ -105,16 +105,8 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::map)) map->emplace(*static_cast(key), std::move(*static_cast(value))); }); - dictionaryTrait.setErase([](void* instance, const void* iterator, bool writeable) { - auto* map = static_cast(instance); - if (writeable) - { - map->erase(*static_cast(iterator)); - } - else - { - map->erase(*static_cast(iterator)); - } + dictionaryTrait.setErase([](void* instance, const void* iterator) { + static_cast(instance)->erase(*static_cast(iterator)); }); return Type::create("std::map<" + reflect().name() + ", " + reflect().name() + ">") diff --git a/core/include/cubos/core/reflection/external/unordered_map.hpp b/core/include/cubos/core/reflection/external/unordered_map.hpp index 39183367d8..b9f016e90f 100644 --- a/core/include/cubos/core/reflection/external/unordered_map.hpp +++ b/core/include/cubos/core/reflection/external/unordered_map.hpp @@ -99,22 +99,14 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map, we always supply this function. + // Since V must be moveable for all std::unordered_map, we always supply this function. dictionaryTrait.setInsertMove([](void* instance, const void* key, void* value) { auto* map = static_cast(instance); map->emplace(*static_cast(key), std::move(*static_cast(value))); }); - dictionaryTrait.setErase([](void* instance, const void* iterator, bool writeable) { - auto* map = static_cast(instance); - if (writeable) - { - map->erase(*static_cast(iterator)); - } - else - { - map->erase(*static_cast(iterator)); - } + dictionaryTrait.setErase([](void* instance, const void* iterator) { + static_cast(instance)->erase(*static_cast(iterator)); }); return Type::create("std::unordered_map<" + reflect().name() + ", " + reflect().name() + ">") diff --git a/core/include/cubos/core/reflection/traits/dictionary.hpp b/core/include/cubos/core/reflection/traits/dictionary.hpp index c48bf2739e..a24c85c232 100644 --- a/core/include/cubos/core/reflection/traits/dictionary.hpp +++ b/core/include/cubos/core/reflection/traits/dictionary.hpp @@ -17,6 +17,12 @@ namespace cubos::core::reflection class DictionaryTrait final { public: + /// @brief Provides mutable access to a dictionary. + class View; + + /// @brief Provides immutable access to a dictionary. + class ConstView; + /// @brief Points to a key-value pair in a dictionary, allowing modification of the value. class Iterator; @@ -87,9 +93,8 @@ namespace cubos::core::reflection /// @brief Function pointer to remove a key-value pair of a dictionary instance. /// @param instance Dictionary instance. - /// @param iterator Iterator to the key-value pair. - /// @param writeable Whether the iterator provides write access. - using Erase = void (*)(void* instance, const void* iterator, bool writeable); + /// @param iterator Iterator to the key-value pair with write access. + using Erase = void (*)(void* instance, const void* iterator); /// @brief Constructs. /// @param keyType Key type of the dictionary. @@ -120,65 +125,6 @@ namespace cubos::core::reflection /// @param erase Function pointer. void setErase(Erase erase); - /// @brief Returns the key type of the dictionary. - /// @return Key type. - const Type& keyType() const; - - /// @brief Returns the value type of the dictionary. - /// @return Value type. - const Type& valueType() const; - - /// @brief Returns the length of the given dictionary. - /// @param instance Dictionary instance. - /// @return Dictionary length. - std::size_t length(const void* instance) const; - - /// @brief Returns an iterator to the beginning of the given dictionary. - /// @param instance Dictionary instance. - /// @return Iterator. - Iterator begin(void* instance) const; - - /// @copydoc begin(void*) const - ConstIterator begin(const void* instance) const; - - /// @brief Returns an iterator to the element of the given dictionary with the given key. - /// @param instance Dictionary instance. - /// @param key Key. - /// @return Iterator to the found key-value pair, or null if not found. - Iterator find(void* instance, const void* key) const; - - /// @copydoc find(void*, const void*) const - ConstIterator find(const void* instance, const void* key) const; - - /// @brief Inserts a default-constructed value into the dicitonary. - /// @param instance Dictionary instance. - /// @param key Key. - /// @return Whether the operation is supported. - bool insertDefault(void* instance, const void* key) const; - - /// @brief Inserts a copy-constructed value into the dictionary. - /// @param instance Dictionary instance. - /// @param key Key. - /// @param value Value. - /// @return Whether the operation is supported. - bool insertCopy(void* instance, const void* key, const void* value) const; - - /// @brief Inserts a move-constructed value into the dictionary. - /// @param instance Dictionary instance. - /// @param key Key. - /// @param value Value. - /// @return Whether the operation is supported. - bool insertMove(void* instance, const void* key, void* value) const; - - /// @brief Removes a key-value pair of the dictionary. - /// @param instance Dictionary instance. - /// @param key Iterator. - /// @return Whether the operation is supported. - bool erase(void* instance, Iterator& iterator) const; - - /// @copydoc erase(void*, Iterator&) const - bool erase(void* instance, ConstIterator& iterator) const; - /// @brief Checks if default-construct insert is supported. /// @return Whether the operation is supported. bool hasInsertDefault() const; @@ -195,8 +141,25 @@ namespace cubos::core::reflection /// @return Whether the operation is supported. bool hasErase() const; + /// @brief Returns the key type of the dictionary. + /// @return Key type. + const Type& keyType() const; + + /// @brief Returns the value type of the dictionary. + /// @return Value type. + const Type& valueType() const; + + /// @brief Returns a view of the given dictionary instance. + /// @param instance Dictionary instance. + /// @return Dictionary view. + View view(void* instance) const; + + /// @copydoc view(void*) const + ConstView view(const void* instance) const; + private: - friend Iterator; + friend View; + friend ConstView; const Type& mKeyType; const Type& mValueType; @@ -213,9 +176,106 @@ namespace cubos::core::reflection Erase mErase{nullptr}; }; - class DictionaryTrait::Iterator + class DictionaryTrait::View { public: + /// @brief Used to iterate over the entries of a dictionary. + class Iterator; + + /// @brief Constructs. + /// @param trait Trait. + /// @param instance Instance. + View(const DictionaryTrait& trait, void* instance); + + /// @brief Returns the length of the dictionary. + /// @return Dictionary length. + std::size_t length() const; + + /// @brief Returns an iterator to the first entry. + /// @return Iterator. + Iterator begin() const; + + /// @brief Returns an iterator to the entry after the last entry. + /// @return Iterator. + Iterator end() const; + + /// @brief Returns an iterator to the entry with the given key. + /// @note If no entry with the given key exists, @ref end() is returned. + /// @param key Key. + /// @return Iterator. + Iterator find(const void* key) const; + + /// @brief Inserts a default-constructed value with the given key. + /// @note Aborts if @ref DictionaryTrait::hasInsertDefault() returns false. + /// @param key Key. + void insertDefault(const void* key) const; + + /// @brief Inserts a copy-constructed value with the given key. + /// @note Aborts if @ref DictionaryTrait::hasInsertCopy() returns false. + /// @param key Key. + /// @param value Value. + void insertCopy(const void* key, const void* value) const; + + /// @brief Inserts a move-constructed value with the given key. + /// @note Aborts if @ref DictionaryTrait::hasInsertMove() returns false. + /// @param key Key. + /// @param value Value. + void insertMove(const void* key, void* value) const; + + /// @brief Removes an entry. + /// @note Aborts if @ref DictionaryTrait::hasErase() returns false. + /// @param iterator Iterator. + void erase(Iterator& iterator) const; + + private: + const DictionaryTrait& mTrait; + void* mInstance; + }; + + class DictionaryTrait::ConstView + { + public: + /// @brief Used to iterate over the entries of a dictionary. + class Iterator; + + /// @brief Constructs. + /// @param trait Trait. + /// @param instance Instance. + ConstView(const DictionaryTrait& trait, const void* instance); + + /// @brief Returns the length of the dictionary. + /// @return Dictionary length. + std::size_t length() const; + + /// @brief Returns an iterator to the first entry. + /// @return Iterator. + Iterator begin() const; + + /// @brief Returns an iterator to the entry after the last entry. + /// @return Iterator. + Iterator end() const; + + /// @brief Returns an iterator to the entry with the given key. + /// @note If no entry with the given key exists, @ref end() is returned. + /// @param key Key. + /// @return Iterator. + Iterator find(const void* key) const; + + private: + const DictionaryTrait& mTrait; + const void* mInstance; + }; + + class DictionaryTrait::View::Iterator + { + public: + /// @brief Output structure for the iterator. + struct Entry + { + const void* key; ///< Key. + void* value; ///< Value. + }; + ~Iterator(); /// @brief Copy constructs. @@ -224,84 +284,101 @@ namespace cubos::core::reflection /// @brief Move constructs. /// @param other Other iterator. - Iterator(Iterator&& other) noexcept ; + Iterator(Iterator&& other) noexcept; + + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to the same entry. + bool operator==(const Iterator& other) const; - /// @brief Gets a pointer to the current key. - /// @note Aborts if @ref isNull() returns true. - /// @return Key. - const void* key() const; + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to different entries. + bool operator!=(const Iterator&) const; - /// @brief Gets a pointer to the current value. - /// @note Aborts if @ref isNull() returns true. - /// @return Value. - void* value() const; + /// @brief Accesses the entry referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Entry. + const Entry& operator*() const; - /// @brief Advances the iterator. - /// @note Aborts if @ref isNull() returns true. - /// @return Whether the iterator is still valid. - bool advance(); + /// @brief Accesses the entry referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Entry. + const Entry* operator->() const; - /// @brief Checks if the iterator is null. - /// @return Whether the iterator is null. - bool isNull() const; + /// @brief Advances the iterator. + /// @note Aborts if out of bounds. + /// @return Reference to this. + Iterator& operator++(); private: friend DictionaryTrait; /// @brief Constructs. + /// @param view View. /// @param inner Inner iterator. - /// @param instance Dictionary instance. - /// @param trait Dictionary trait. - Iterator(void* inner, void* instance, const DictionaryTrait& trait); + Iterator(const View& view, void* inner); + const View& mView; void* mInner{nullptr}; - void* mInstance; - const DictionaryTrait& mTrait; + mutable Entry mEntry; }; - class DictionaryTrait::ConstIterator + class DictionaryTrait::ConstView::Iterator { public: - ~ConstIterator(); + /// @brief Output structure for the iterator. + struct Entry + { + const void* key; ///< Key. + const void* value; ///< Value. + }; + + ~Iterator(); /// @brief Copy constructs. /// @param other Other iterator. - ConstIterator(const ConstIterator& other); + Iterator(const Iterator& other); /// @brief Move constructs. /// @param other Other iterator. - ConstIterator(ConstIterator&& other) noexcept ; + Iterator(Iterator&& other) noexcept; - /// @brief Gets a pointer to the current key. - /// @note Aborts if @ref isNull() returns true. - /// @return Key. - const void* key() const; + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to the same entry. + bool operator==(const Iterator& other) const; - /// @brief Gets a pointer to the current value. - /// @note Aborts if @ref isNull() returns true. - /// @return Value. - const void* value() const; + /// @brief Compares two iterators. + /// @param other Other iterator. + /// @return Whether the iterators point to different entries. + bool operator!=(const Iterator&) const; - /// @brief Advances the iterator. - /// @note Aborts if @ref isNull() returns true. - /// @return Whether the iterator is still valid. - bool advance(); + /// @brief Accesses the entry referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Entry. + const Entry& operator*() const; - /// @brief Checks if the iterator is null. - /// @return Whether the iterator is null. - bool isNull() const; + /// @brief Accesses the entry referenced by this iterator. + /// @note Aborts if out of bounds. + /// @return Entry. + const Entry* operator->() const; + + /// @brief Advances the iterator. + /// @note Aborts if out of bounds. + /// @return Reference to this. + Iterator& operator++(); private: friend DictionaryTrait; /// @brief Constructs. + /// @param view View. /// @param inner Inner iterator. - /// @param instance Dictionary instance. - /// @param trait Dictionary trait. - ConstIterator(void* inner, const void* instance, const DictionaryTrait& trait); + Iterator(const ConstView& view, void* inner); + const ConstView& mView; void* mInner{nullptr}; - const void* mInstance; - const DictionaryTrait& mTrait; + mutable Entry mEntry; }; } // namespace cubos::core::reflection diff --git a/core/samples/reflection/traits/dictionary/main.cpp b/core/samples/reflection/traits/dictionary/main.cpp index 218bbed768..c451bf7149 100644 --- a/core/samples/reflection/traits/dictionary/main.cpp +++ b/core/samples/reflection/traits/dictionary/main.cpp @@ -13,20 +13,21 @@ void printDictionary(const Type& type, const void* instance) /// [Printing any dictionary] /// [Getting dictionary length and types] - CUBOS_INFO("Dictionary with {} entries of key type {} and value type {}", dictionaryTrait.length(instance), + auto dictionaryView = dictionaryTrait.view(instance); + CUBOS_INFO("Dictionary with {} entries of key type {} and value type {}", dictionaryView.length(), dictionaryTrait.keyType().name(), dictionaryTrait.valueType().name()); /// [Getting dictionary length and types] /// [Getting dictionary entries] if (!dictionaryTrait.keyType().is() || !dictionaryTrait.valueType().is()) { - CUBOS_INFO("This function does not support printing dictionary with key and value types other than int"); + CUBOS_INFO("This function does not support printing dictionary with key and value types other than int32_t"); return; } - for (auto it = dictionaryTrait.begin(instance); !it.isNull(); it.advance()) + for (auto [key, value] : dictionaryView) { - CUBOS_INFO("{} -> {}", *static_cast(it.key()), *static_cast(it.value())); + CUBOS_INFO("{} -> {}", *static_cast(key), *static_cast(value)); } } /// [Getting dictionary entries] diff --git a/core/src/cubos/core/reflection/traits/dictionary.cpp b/core/src/cubos/core/reflection/traits/dictionary.cpp index b265b3c484..71f7a82860 100644 --- a/core/src/cubos/core/reflection/traits/dictionary.cpp +++ b/core/src/cubos/core/reflection/traits/dictionary.cpp @@ -42,248 +42,252 @@ void DictionaryTrait::setErase(Erase erase) mErase = erase; } -const Type& DictionaryTrait::keyType() const +bool DictionaryTrait::hasInsertDefault() const { - return mKeyType; + return mInsertDefault != nullptr; } -const Type& DictionaryTrait::valueType() const +bool DictionaryTrait::hasInsertCopy() const { - return mValueType; + return mInsertCopy != nullptr; } -std::size_t DictionaryTrait::length(const void* instance) const +bool DictionaryTrait::hasInsertMove() const { - return mLength(instance); + return mInsertMove != nullptr; } -DictionaryTrait::Iterator DictionaryTrait::begin(void* instance) const +bool DictionaryTrait::hasErase() const { - return Iterator{mBegin(reinterpret_cast(instance), true), instance, *this}; + return mErase != nullptr; } -DictionaryTrait::ConstIterator DictionaryTrait::begin(const void* instance) const +const Type& DictionaryTrait::keyType() const { - return ConstIterator{mBegin(reinterpret_cast(instance), false), instance, *this}; + return mKeyType; } -DictionaryTrait::Iterator DictionaryTrait::find(void* instance, const void* key) const +const Type& DictionaryTrait::valueType() const { - return Iterator{mFind(reinterpret_cast(instance), key, true), instance, *this}; + return mValueType; } -DictionaryTrait::ConstIterator DictionaryTrait::find(const void* instance, const void* key) const +DictionaryTrait::View DictionaryTrait::view(void* instance) const { - return ConstIterator{mFind(reinterpret_cast(instance), key, false), instance, *this}; + return View{*this, instance}; } -bool DictionaryTrait::insertDefault(void* instance, const void* key) const +DictionaryTrait::ConstView DictionaryTrait::view(const void* instance) const { - if (mInsertDefault != nullptr) - { - mInsertDefault(instance, key); - return true; - } - - return false; + return ConstView{*this, instance}; } -bool DictionaryTrait::insertCopy(void* instance, const void* key, const void* value) const +DictionaryTrait::View::View(const DictionaryTrait& trait, void* instance) + : mTrait(trait) + , mInstance(instance) { - if (mInsertCopy != nullptr) - { - mInsertCopy(instance, key, value); - return true; - } +} - return false; +std::size_t DictionaryTrait::View::length() const +{ + return mTrait.mLength(mInstance); } -bool DictionaryTrait::insertMove(void* instance, const void* key, void* value) const +DictionaryTrait::View::Iterator DictionaryTrait::View::begin() const { - if (mInsertMove != nullptr) - { - mInsertMove(instance, key, value); - return true; - } + return Iterator{*this, mTrait.mBegin(reinterpret_cast(mInstance), true)}; +} - return false; +DictionaryTrait::View::Iterator DictionaryTrait::View::end() const +{ + return Iterator{*this, nullptr}; } -bool DictionaryTrait::erase(void* instance, Iterator& iterator) const +DictionaryTrait::View::Iterator DictionaryTrait::View::find(const void* key) const { - CUBOS_ASSERT(!iterator.isNull(), "Cannot erase null iterator"); + return Iterator{*this, mTrait.mFind(reinterpret_cast(mInstance), key, true)}; +} - if (mErase != nullptr) - { - mErase(instance, iterator.mInner, true); - mStop(iterator.mInner, true); - iterator.mInner = nullptr; - return true; - } +void DictionaryTrait::View::insertDefault(const void* key) const +{ + CUBOS_ASSERT(mTrait.hasInsertDefault(), "Insert default not supported"); + mTrait.mInsertDefault(mInstance, key); +} - return false; +void DictionaryTrait::View::insertCopy(const void* key, const void* value) const +{ + CUBOS_ASSERT(mTrait.hasInsertCopy(), "Insert copy not supported"); + mTrait.mInsertCopy(mInstance, key, value); } -bool DictionaryTrait::erase(void* instance, ConstIterator& iterator) const +void DictionaryTrait::View::insertMove(const void* key, void* value) const { - CUBOS_ASSERT(!iterator.isNull(), "Cannot erase null iterator"); + CUBOS_ASSERT(mTrait.hasInsertMove(), "Insert move not supported"); + mTrait.mInsertMove(mInstance, key, value); +} - if (mErase != nullptr) - { - mErase(instance, iterator.mInner, false); - mStop(iterator.mInner, false); - iterator.mInner = nullptr; - return true; - } +void DictionaryTrait::View::erase(Iterator& iterator) const +{ + CUBOS_ASSERT(mTrait.hasErase(), "Erase not supported"); + mTrait.mErase(mInstance, iterator.mInner); + mTrait.mStop(iterator.mInner, true); + iterator.mInner = nullptr; +} - return false; +DictionaryTrait::ConstView::ConstView(const DictionaryTrait& trait, const void* instance) + : mTrait(trait) + , mInstance(instance) +{ } -bool DictionaryTrait::hasInsertDefault() const +std::size_t DictionaryTrait::ConstView::length() const { - return mInsertDefault != nullptr; + return mTrait.mLength(mInstance); } -bool DictionaryTrait::hasInsertCopy() const +DictionaryTrait::ConstView::Iterator DictionaryTrait::ConstView::begin() const { - return mInsertCopy != nullptr; + return Iterator{*this, mTrait.mBegin(reinterpret_cast(mInstance), false)}; } -bool DictionaryTrait::hasInsertMove() const +DictionaryTrait::ConstView::Iterator DictionaryTrait::ConstView::end() const { - return mInsertMove != nullptr; + return Iterator{*this, nullptr}; } -bool DictionaryTrait::hasErase() const +DictionaryTrait::ConstView::Iterator DictionaryTrait::ConstView::find(const void* key) const { - return mErase != nullptr; + return Iterator{*this, mTrait.mFind(reinterpret_cast(mInstance), key, false)}; } -DictionaryTrait::Iterator::~Iterator() +DictionaryTrait::View::Iterator::~Iterator() { if (mInner != nullptr) { - mTrait.mStop(mInner, true); + mView.mTrait.mStop(mInner, true); } } -DictionaryTrait::Iterator::Iterator(void* inner, void* instance, const DictionaryTrait& trait) - : mInner(inner) - , mInstance(instance) - , mTrait(trait) +DictionaryTrait::View::Iterator::Iterator(const View& view, void* inner) + : mView(view) + , mInner(inner) { } -DictionaryTrait::Iterator::Iterator(const Iterator& other) - : mInstance(other.mInstance) - , mTrait(other.mTrait) +DictionaryTrait::View::Iterator::Iterator(const Iterator& other) + : mView(other.mView) { - if (!other.isNull()) + if (other.mInner != nullptr) { - mInner = mTrait.mFind(reinterpret_cast(mInstance), other.key(), true); + mInner = mView.mTrait.mFind(reinterpret_cast(mView.mInstance), other->key, true); } } -DictionaryTrait::Iterator::Iterator(Iterator&& other) noexcept - : mInner(other.mInner) - , mInstance(other.mInstance) - , mTrait(other.mTrait) +DictionaryTrait::View::Iterator::Iterator(Iterator&& other) noexcept + : mView(other.mView) + , mInner(other.mInner) { other.mInner = nullptr; } -const void* DictionaryTrait::Iterator::key() const +bool DictionaryTrait::View::Iterator::operator==(const Iterator& other) const { - CUBOS_ASSERT(mInner != nullptr, "Cannot get key from null iterator"); - return mTrait.mKey(mInner, true); + return mInner == other.mInner || (mInner != nullptr && other.mInner != nullptr && (*this)->value == other->value); } -void* DictionaryTrait::Iterator::value() const +bool DictionaryTrait::View::Iterator::operator!=(const Iterator& other) const { - CUBOS_ASSERT(mInner != nullptr, "Cannot get value from null iterator"); - return reinterpret_cast(mTrait.mValue(mInner, true)); + return !(*this == other); } -bool DictionaryTrait::Iterator::advance() +const DictionaryTrait::View::Iterator::Entry& DictionaryTrait::View::Iterator::operator*() const { - CUBOS_ASSERT(mInner != nullptr, "Cannot advance null iterator"); - - if (mTrait.mAdvance(reinterpret_cast(mInstance), mInner, true)) - { - return true; - } + CUBOS_ASSERT(mInner != nullptr, "Iterator out of bounds"); + mEntry.key = mView.mTrait.mKey(mInner, true); + mEntry.value = reinterpret_cast(mView.mTrait.mValue(mInner, true)); + return mEntry; +} - mTrait.mStop(mInner, true); - mInner = nullptr; - return false; +const DictionaryTrait::View::Iterator::Entry* DictionaryTrait::View::Iterator::operator->() const +{ + return &this->operator*(); } -bool DictionaryTrait::Iterator::isNull() const +DictionaryTrait::View::Iterator& DictionaryTrait::View::Iterator::operator++() { - return mInner == nullptr; + CUBOS_ASSERT(mInner != nullptr, "Iterator out of bounds"); + + if (!mView.mTrait.mAdvance(reinterpret_cast(mView.mInstance), mInner, true)) + { + mView.mTrait.mStop(mInner, true); + mInner = nullptr; + } + + return *this; } -DictionaryTrait::ConstIterator::~ConstIterator() +DictionaryTrait::ConstView::Iterator::~Iterator() { if (mInner != nullptr) { - mTrait.mStop(mInner, false); + mView.mTrait.mStop(mInner, false); } } -DictionaryTrait::ConstIterator::ConstIterator(void* inner, const void* instance, const DictionaryTrait& trait) - : mInner(inner) - , mInstance(instance) - , mTrait(trait) +DictionaryTrait::ConstView::Iterator::Iterator(const ConstView& view, void* inner) + : mView(view) + , mInner(inner) { } -DictionaryTrait::ConstIterator::ConstIterator(const ConstIterator& other) - : mInstance(other.mInstance) - , mTrait(other.mTrait) +DictionaryTrait::ConstView::Iterator::Iterator(const Iterator& other) + : mView(other.mView) { - if (!other.isNull()) + if (other.mInner != nullptr) { - mInner = mTrait.mFind(reinterpret_cast(mInstance), other.key(), false); + mInner = mView.mTrait.mFind(reinterpret_cast(mView.mInstance), other->key, false); } } -DictionaryTrait::ConstIterator::ConstIterator(ConstIterator&& other) noexcept - : mInner(other.mInner) - , mInstance(other.mInstance) - , mTrait(other.mTrait) +DictionaryTrait::ConstView::Iterator::Iterator(Iterator&& other) noexcept + : mView(other.mView) + , mInner(other.mInner) { other.mInner = nullptr; } -const void* DictionaryTrait::ConstIterator::key() const +bool DictionaryTrait::ConstView::Iterator::operator==(const Iterator& other) const { - CUBOS_ASSERT(mInner != nullptr, "Cannot get key from null iterator"); - return mTrait.mKey(mInner, false); + return mInner == other.mInner || (mInner != nullptr && other.mInner != nullptr && (*this)->value == other->value); } -const void* DictionaryTrait::ConstIterator::value() const +bool DictionaryTrait::ConstView::Iterator::operator!=(const Iterator& other) const { - CUBOS_ASSERT(mInner != nullptr, "Cannot get value from null iterator"); - return reinterpret_cast(mTrait.mValue(mInner, false)); + return !(*this == other); } -bool DictionaryTrait::ConstIterator::advance() +const DictionaryTrait::ConstView::Iterator::Entry& DictionaryTrait::ConstView::Iterator::operator*() const { - CUBOS_ASSERT(mInner != nullptr, "Cannot advance null iterator"); - - if (mTrait.mAdvance(reinterpret_cast(mInstance), mInner, false)) - { - return true; - } + CUBOS_ASSERT(mInner != nullptr, "Iterator out of bounds"); + mEntry.key = mView.mTrait.mKey(mInner, false); + mEntry.value = reinterpret_cast(mView.mTrait.mValue(mInner, false)); + return mEntry; +} - mTrait.mStop(mInner, false); - mInner = nullptr; - return false; +const DictionaryTrait::ConstView::Iterator::Entry* DictionaryTrait::ConstView::Iterator::operator->() const +{ + return &this->operator*(); } -bool DictionaryTrait::ConstIterator::isNull() const +DictionaryTrait::ConstView::Iterator& DictionaryTrait::ConstView::Iterator::operator++() { - return mInner == nullptr; + CUBOS_ASSERT(mInner != nullptr, "Iterator out of bounds"); + + if (!mView.mTrait.mAdvance(reinterpret_cast(mView.mInstance), mInner, true)) + { + mView.mTrait.mStop(mInner, true); + mInner = nullptr; + } + + return *this; } diff --git a/core/tests/reflection/traits/dictionary.hpp b/core/tests/reflection/traits/dictionary.hpp index afc507a81a..672ea9f3ae 100644 --- a/core/tests/reflection/traits/dictionary.hpp +++ b/core/tests/reflection/traits/dictionary.hpp @@ -24,87 +24,94 @@ void testDictionary(T& value, std::size_t length, const K* insertedKey, V* inser REQUIRE(type.has()); const DictionaryTrait& trait = type.get(); - REQUIRE(trait.length(&value) == length); REQUIRE(trait.keyType().is()); REQUIRE(trait.valueType().is()); + auto view = trait.view(&value); + auto constView = trait.view(static_cast(&value)); + REQUIRE(view.length() == length); + REQUIRE(constView.length() == length); + // Check that both the normal and const iterators count the expected number of elements. std::size_t i = 0; - for (auto it = trait.begin(&value); !it.isNull(); it.advance()) + for (auto entry : view) { + (void)entry; i += 1; } REQUIRE(i == length); i = 0; - for (auto it = trait.begin(static_cast(&value)); !it.isNull(); it.advance()) + for (auto entry : constView) { + (void)entry; i += 1; } REQUIRE(i == length); if (trait.hasInsertDefault()) { - REQUIRE(trait.find(&value, insertedKey).isNull()); - REQUIRE(trait.insertDefault(&value, insertedKey)); - auto it = trait.find(static_cast(&value), insertedKey); - REQUIRE(!it.isNull()); + + REQUIRE(constView.find(insertedKey) == constView.end()); + view.insertDefault(insertedKey); + auto it = view.find(insertedKey); + REQUIRE(it != view.end()); if constexpr (std::equality_comparable) { - CHECK(*static_cast(it.key()) == *insertedKey); + CHECK(*static_cast(it->key) == *insertedKey); } if constexpr (std::equality_comparable && std::default_initializable) { - CHECK(*static_cast(it.value()) == V{}); + CHECK(*static_cast(it->value) == V{}); } - REQUIRE(trait.length(&value) == length + 1); - REQUIRE(trait.erase(&value, it)); - REQUIRE(trait.length(&value) == length); - REQUIRE(trait.find(&value, insertedKey).isNull()); + REQUIRE(view.length() == length + 1); + view.erase(it); + REQUIRE(view.length() == length); + REQUIRE(view.find(insertedKey) == view.end()); } if (trait.hasInsertCopy()) { - REQUIRE(trait.find(&value, insertedKey).isNull()); - REQUIRE(trait.insertCopy(&value, insertedKey, insertedValue)); - auto it = trait.find(&value, insertedKey); - REQUIRE(!it.isNull()); + REQUIRE(view.find(insertedKey) == view.end()); + view.insertCopy(insertedKey, insertedValue); + auto it = view.find(insertedKey); + REQUIRE(it != view.end()); if constexpr (std::equality_comparable) { - CHECK(*static_cast(it.key()) == *insertedKey); + CHECK(*static_cast(it->key) == *insertedKey); } if constexpr (std::equality_comparable) { - CHECK(*static_cast(it.value()) == *static_cast(insertedValue)); + CHECK(*static_cast(it->value) == *static_cast(insertedValue)); } - REQUIRE(trait.length(&value) == length + 1); - REQUIRE(trait.erase(&value, it)); - REQUIRE(trait.length(&value) == length); - REQUIRE(trait.find(&value, insertedKey).isNull()); + REQUIRE(view.length() == length + 1); + view.erase(it); + REQUIRE(view.length() == length); + REQUIRE(view.find(insertedKey) == view.end()); } if (trait.hasInsertMove()) { - REQUIRE(trait.find(&value, insertedKey).isNull()); - REQUIRE(trait.insertMove(&value, insertedKey, insertedValue)); - auto it = trait.find(static_cast(&value), insertedKey); - REQUIRE(!it.isNull()); + REQUIRE(view.find(insertedKey) == view.end()); + view.insertMove(insertedKey, insertedValue); + auto it = view.find(insertedKey); + REQUIRE(it != view.end()); if constexpr (std::equality_comparable) { - CHECK(*static_cast(it.key()) == *insertedKey); + CHECK(*static_cast(it->key) == *insertedKey); } - REQUIRE(trait.length(&value) == length + 1); - REQUIRE(trait.erase(&value, it)); - REQUIRE(trait.length(&value) == length); - REQUIRE(trait.find(&value, insertedKey).isNull()); + REQUIRE(view.length() == length + 1); + view.erase(it); + REQUIRE(view.length() == length); + REQUIRE(view.find(insertedKey) == view.end()); } } From 20be5a0f35709295841569d5bc12cb665e187c91 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Thu, 28 Sep 2023 11:02:10 +0100 Subject: [PATCH 12/12] docs(core): add missing parameter names Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/include/cubos/core/reflection/traits/dictionary.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/include/cubos/core/reflection/traits/dictionary.hpp b/core/include/cubos/core/reflection/traits/dictionary.hpp index a24c85c232..76a51aa2a7 100644 --- a/core/include/cubos/core/reflection/traits/dictionary.hpp +++ b/core/include/cubos/core/reflection/traits/dictionary.hpp @@ -294,7 +294,7 @@ namespace cubos::core::reflection /// @brief Compares two iterators. /// @param other Other iterator. /// @return Whether the iterators point to different entries. - bool operator!=(const Iterator&) const; + bool operator!=(const Iterator& /*other*/) const; /// @brief Accesses the entry referenced by this iterator. /// @note Aborts if out of bounds. @@ -352,7 +352,7 @@ namespace cubos::core::reflection /// @brief Compares two iterators. /// @param other Other iterator. /// @return Whether the iterators point to different entries. - bool operator!=(const Iterator&) const; + bool operator!=(const Iterator& /*other*/) const; /// @brief Accesses the entry referenced by this iterator. /// @note Aborts if out of bounds.