Skip to content

Commit

Permalink
test(core): std::unordered_map reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
RiscadoA committed Jun 18, 2023
1 parent 0dd2491 commit b804b38
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 2 deletions.
92 changes: 90 additions & 2 deletions core/include/cubos/core/reflection/dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,49 @@ namespace cubos::core::reflection
/// @return Key which the iterator is currently pointing to.
const void* key(const void* dictionary) const;

/// @brief Gets the key which the iterator is currently pointing to. Aborts if the
/// iterator is at the end of the dictionary. If K does not match the key type of the
/// dictionary, the behaviour is undefined.
/// @tparam K Type of the key.
/// @param dictionary Instance of the dictionary.
/// @return Key which the iterator is currently pointing to.
template <typename K>
const K& key(const void* dictionary) const
{
return *static_cast<const K*>(this->key(dictionary));
}

/// @name Getters for the value which the iterator is currently pointing to.
/// @brief Gets the value which the iterator is currently pointing to.
/// @brief Gets the value which the iterator is currently pointing to. Aborts if the
/// iterator is at the end of the dictionary.
/// @param dictionary Instance of the dictionary.
/// @return Value which the iterator is currently pointing to.
/// @{
const void* value(const void* dictionary) const;
void* value(void* dictionary) const;
/// @}

/// @name Template getters for the value which the iterator is currently pointing to.
/// @brief Gets the value which the iterator is currently pointing to. Aborts if the
/// iterator is at the end of the dictionary. If T does not match the value type of the
/// dictionary, the behaviour is undefined.
/// @tparam V Type of the value.
/// @param dictionary Instance of the dictionary.
/// @return Value which the iterator is currently pointing to.
/// @{
template <typename V>
const V& value(const void* dictionary) const
{
return *static_cast<const V*>(this->value(dictionary));
}

template <typename V>
V& value(void* dictionary) const
{
return *static_cast<V*>(this->value(dictionary));
}
/// @}

/// @brief Increments the iterator.
/// @param dictionary Instance of the dictionary.
void increment(const void* dictionary);
Expand Down Expand Up @@ -181,12 +215,36 @@ namespace cubos::core::reflection
/// instance of this type, otherwise, undefined behavior occurs.
/// @param dictionary Pointer to the dictionary.
/// @param key Instance of the key type to get the value for.
/// @return Address of the value, or `nullptr` if the key does not exist.
/// @return Pointer to the value, or `nullptr` if the key does not exist.
/// @{
const void* value(const void* dictionary, const void* key) const;
void* value(void* dictionary, const void* key) const;
/// @}

/// @name Templated value access methods.
/// @brief Gets the value with the given key, or `nullptr` if the key does not exist.
/// Aborts if the value getter function is not set. The given dictionary must be a valid
/// instance of this type and K and V must match the key and value types of the dictionary,
/// otherwise, undefined behaviour occurs.
/// @tparam K Type of the keys.
/// @tparam V Type of the values.
/// @param dictionary Pointer to the dictionary.
/// @param key Instance of the key to get the value for.
/// @return Pointer to the vlaue, or `nullptr` if the key does not exist.
/// @{
template <typename K, typename V>
const V* value(const void* dictionary, const K& key) const
{
return static_cast<const V*>(this->value(dictionary, &key));
}

template <typename K, typename V>
V* value(void* dictionary, const K& key) const
{
return static_cast<V*>(this->value(dictionary, &key));
}
/// @}

/// @brief Inserts the given key into the dictionary, with a default constructed value.
/// If the key already exists, the value is not changed and its address is returned.
/// Aborts if the insert function is not set. The given dictionary must be a valid instance
Expand All @@ -196,6 +254,22 @@ namespace cubos::core::reflection
/// @return Address of the value.
void* insert(void* dictionary, const void* key) const;

/// @brief Inserts the given key into the dictionary, with a default constructed value.
/// If the key already exists, the value is not changed and its address is returned.
/// Aborts if the insert function is not set. The given dictionary must be a valid instance
/// of this type and K must match the key type of the dictionary, otherwise, undefined
/// behavior occurs.
/// @tparam K Type of the keys.
/// @tparam V Type of the values.
/// @param dictionary Pointer to the dictionary.
/// @param key Instance of the key type to insert.
/// @return Address of the value.
template <typename K, typename V>
V& insert(void* dictionary, const K& key) const
{
return *static_cast<V*>(this->insert(dictionary, static_cast<const void*>(&key)));
}

/// @brief Removes the value with the given key from the dictionary.
/// Aborts if the remove function is not set. The given dictionary must be a valid instance
/// of this type, otherwise, undefined behavior occurs.
Expand All @@ -204,6 +278,20 @@ namespace cubos::core::reflection
/// @return `true` if the key existed and was removed, `false` otherwise.
bool remove(void* dictionary, const void* key) const;

/// @brief Removes the value with the given key from the dictionary.
/// Aborts if the remove function is not set. The given dictionary must be a valid instance
/// of this type and K must match the key type of the dictionary, otherwise, undefined
/// behavior occurs.
/// @tparam K Type of the keys.
/// @param dictionary Pointer to the dictionary.
/// @param key Instance of the key type to remove.
/// @return `true` if the key existed and was removed, `false` otherwise.
template <typename K>
bool remove(void* dictionary, const K& key) const
{
return this->remove(dictionary, static_cast<const void*>(&key));
}

/// @brief Creates an iterator for the given dictionary.
/// Aborts if hasIterator() returns `false`. The given dictionary must be a valid instance
/// of this type, otherwise, undefined behavior occurs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@ CUBOS_REFLECT_EXTERNAL_TEMPLATE((typename K, typename V), (std::unordered_map<K,
return false;
})
.iteratorDelete([](void* iterator) { delete static_cast<Iterator*>(iterator); })
.template defaultConstructible<std::unordered_map<K, V>>()
.get();
}
7 changes: 7 additions & 0 deletions core/src/cubos/core/reflection/dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ const void* DictionaryType::Iterator::value(const void* dictionary) const
return reinterpret_cast<const void*>(mType.mIteratorValue(dictionary, mIterator));
}

void* DictionaryType::Iterator::value(void* dictionary) const
{
CUBOS_ASSERT(mIterator, "Iterator has already reached the end");
CUBOS_ASSERT(mType.mIteratorValue);
return reinterpret_cast<void*>(mType.mIteratorValue(dictionary, mIterator));
}

void DictionaryType::Iterator::increment(const void* dictionary)
{
CUBOS_ASSERT(mIterator, "Iterator has already reached the end");
Expand Down
1 change: 1 addition & 0 deletions core/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_executable(
reflection/external/primitives_and_string.cpp
reflection/external/variant.cpp
reflection/external/vector.cpp
reflection/external/unordered_map.cpp

data/fs/embedded_archive.cpp
data/fs/standard_archive.cpp
Expand Down
93 changes: 93 additions & 0 deletions core/tests/reflection/external/unordered_map.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include <cubos/core/reflection/external/primitives.hpp>
#include <cubos/core/reflection/external/unordered_map.hpp>

#include "../utils.hpp"

using cubos::core::reflection::DictionaryType;
using cubos::core::reflection::reflect;

template <typename K, typename V>
static void testStaticInfo(const char* name)
{
testTypeGetters<std::unordered_map<K, V>, DictionaryType>(name, "std::unordered_map");
testTypeDefaultConstructor<std::unordered_map<K, V>>();

// Check if it holds the correct element type.
auto& td = reflect<std::unordered_map<K, V>>().template asKind<DictionaryType>();
REQUIRE(td.keyType().template is<K>());
REQUIRE(td.valueType().template is<V>());
}

TEST_CASE("reflection::reflect<std::unordered_map>()")
{
SUBCASE("static information is correct")
{
testStaticInfo<int32_t, bool>("std::unordered_map<int32_t, bool>");
testStaticInfo<bool, std::unordered_map<char, float>>(
"std::unordered_map<bool, std::unordered_map<char, float>>");
}

SUBCASE("inspect and modify an unordered map")
{
std::unordered_map<int32_t, bool> map = {{1, false}, {2, true}};
auto& t = reflect<decltype(map)>().asKind<DictionaryType>();

// Check if the map contains the correct elements, and if they can be modified.
REQUIRE(t.length(&map) == 2);
CHECK(t.value<int32_t, bool>(&map, 0) == nullptr);
CHECK(*t.value<int32_t, bool>(&map, 1) == false);
CHECK(*t.value<int32_t, bool>(&map, 2) == true);
*t.value<int32_t, bool>(&map, 1) = true;
CHECK(map.at(1) == true);

// Inserting an already existing key returns the existing value.
CHECK(t.insert<int32_t, bool>(&map, 1) == true);

// Inserting a new key returns a new default constructed value.
CHECK(t.insert<int32_t, bool>(&map, 0) == bool{});
CHECK(map.at(0) == bool{});
CHECK(t.length(&map) == 3);

// Removing an existing key returns true.
CHECK(t.remove<int32_t>(&map, 1) == true);
CHECK(t.length(&map) == 2);
CHECK(t.value<int32_t, bool>(&map, 1) == nullptr);

// Removing a non-existing key returns false.
CHECK(t.remove<int32_t>(&map, 1) == false);
CHECK(t.length(&map) == 2);
}

SUBCASE("iterate over an unordered map")
{
std::unordered_map<bool, int32_t> map = {};
auto& t = reflect<decltype(map)>().asKind<DictionaryType>();

SUBCASE("empty")
{
}

SUBCASE("not empty")
{
map[false] = 1;
map[true] = 2;
}

// Iterate over the map.
std::unordered_map<bool, int32_t> found = {};
for (auto it = t.iterator(&map); it.valid(); it.increment(&map))
{
auto key = it.key<bool>(&map);
auto value = it.value<int32_t>(&map);
CHECK_FALSE(found.contains(key));
found[key] = value;
}

// Check if all elements were found.
CHECK(found.size() == map.size());
for (auto [key, value] : map)
{
CHECK(found.at(key) == value);
}
}
}

0 comments on commit b804b38

Please sign in to comment.