From 58f22cc3b50fb54ebdd8b2cdcf95e2f61d179ab4 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 8 Feb 2022 10:56:27 +0100 Subject: [PATCH] meta_any: ::hash function for hashable types (+ bonus doc review) --- TODO | 1 + src/entt/meta/meta.hpp | 74 +++++++++++++++++-------------------- test/entt/meta/meta_any.cpp | 27 ++++++++++++++ 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/TODO b/TODO index 44cde4015..c412f1da1 100644 --- a/TODO +++ b/TODO @@ -4,6 +4,7 @@ * add examples (and credits) from @alanjfs :) WIP: +* any: avoid operations when trivially-* * runtime events (emitter) * iterator based try_emplace vs try_insert for perf reasons * registry: remove reference to basic_sparse_set diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index f31975d1a..8fb4f73e9 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -2,6 +2,7 @@ #define ENTT_META_META_HPP #include +#include #include #include #include @@ -283,21 +284,15 @@ public: return *this; } - /** - * @brief Returns the type of the underlying object. - * @return The type of the underlying object, if any. - */ + /*! @copydoc any::type */ [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; - /** - * @brief Returns an opaque pointer to the contained instance. - * @return An opaque pointer the contained instance, if any. - */ + /*! @copydoc any::data */ [[nodiscard]] const void *data() const ENTT_NOEXCEPT { return storage.data(); } - /*! @copydoc data */ + /*! @copydoc any::data */ [[nodiscard]] void *data() ENTT_NOEXCEPT { return storage.data(); } @@ -469,12 +464,7 @@ public: return false; } - /** - * @brief Replaces the contained object by creating a new instance directly. - * @tparam Type Type of object to use to initialize the wrapper. - * @tparam Args Types of arguments to use to construct the new instance. - * @param args Parameters to use to construct the instance. - */ + /*! @copydoc any::emplace */ template void emplace(Args &&...args) { release(); @@ -483,21 +473,13 @@ public: node = internal::meta_node>>::resolve(); } - /** - * @brief Copy assigns a value to the contained object without replacing it. - * @param other The value to assign to the contained object. - * @return True in case of success, false otherwise. - */ + /*! @copydoc any::assign */ bool assign(const meta_any &other); - /** - * @brief Move assigns a value to the contained object without replacing it. - * @param other The value to assign to the contained object. - * @return True in case of success, false otherwise. - */ + /*! @copydoc any::assign */ bool assign(meta_any &&other); - /*! @brief Destroys contained object */ + /*! @copydoc any::reset */ void reset() { release(); vtable = &basic_vtable; @@ -562,36 +544,31 @@ public: return !(node == nullptr); } - /** - * @brief Checks if two wrappers differ in their content. - * @param other Wrapper with which to compare. - * @return False if the two objects differ in their content, true otherwise. - */ + /*! @copydoc any::operator== */ [[nodiscard]] bool operator==(const meta_any &other) const { return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage); } - /** - * @brief Aliasing constructor. - * @return A wrapper that shares a reference to an unmanaged object. - */ + /*! @copydoc any::as_ref */ [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { return meta_any{*this, storage.as_ref()}; } - /*! @copydoc as_ref */ + /*! @copydoc any::as_ref */ [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { return meta_any{*this, storage.as_ref()}; } - /** - * @brief Returns true if a wrapper owns its object, false otherwise. - * @return True if the wrapper owns its object, false otherwise. - */ + /*! @copydoc any::owner */ [[nodiscard]] bool owner() const ENTT_NOEXCEPT { return storage.owner(); } + /*! @copydoc any::hash */ + [[nodiscard]] std::size_t hash() const ENTT_NOEXCEPT { + return storage.hash(); + } + private: any storage; internal::meta_type_node *node; @@ -1867,4 +1844,21 @@ inline bool meta_associative_container::erase(meta_any key) { } // namespace entt +namespace std { + +/*! @brief `std::hash` specialization for `entt::meta_any`. */ +template<> +struct hash { + /** + * @brief Returns the hash value of the parameter. + * @param any The object to return the hash for. + * @return The hash value of the parameter. + */ + [[nodiscard]] std::size_t operator()(const entt::meta_any &any) const ENTT_NOEXCEPT { + return any.hash(); + } +}; + +} // namespace std + #endif diff --git a/test/entt/meta/meta_any.cpp b/test/entt/meta/meta_any.cpp index 1440b6073..80551d073 100644 --- a/test/entt/meta/meta_any.cpp +++ b/test/entt/meta/meta_any.cpp @@ -1076,6 +1076,33 @@ TEST_F(MetaAny, CompareVoid) { ASSERT_TRUE(entt::meta_any{} != any); } +TEST_F(MetaAny, Hashable) { + const int value = 42; + entt::meta_any any{value}; + const entt::meta_any ref{std::in_place_type, value}; + + ASSERT_TRUE(any); + ASSERT_TRUE(ref); + + ASSERT_EQ(any.hash(), std::hash{}(value)); + ASSERT_EQ(std::hash{}(value), std::hash{}(ref)); + ASSERT_EQ(ref.hash(), std::hash{}(any)); +} + +TEST_F(MetaAny, NotHashable) { + const not_comparable_t value{}; + entt::meta_any any{value}; + const entt::meta_any ref{std::in_place_type, value}; + + ASSERT_TRUE(any); + ASSERT_TRUE(ref); + + ASSERT_EQ(any.hash(), std::hash{}({})); + ASSERT_EQ(std::hash{}({}), std::hash{}(ref)); + ASSERT_EQ(ref.hash(), std::hash{}(any)); + ASSERT_EQ(any.hash(), entt::any{}.hash()); +} + TEST_F(MetaAny, TryCast) { entt::meta_any any{fat_t{}};