1
0
Эх сурвалжийг харах

meta: dedicate iterators for different types of meta containers

Michele Caini 5 жил өмнө
parent
commit
ade8533f28

+ 9 - 3
docs/md/meta.md

@@ -585,17 +585,23 @@ differences in behavior in the case of key-only containers. In particular:
   used to iterate the container directly:
 
   ```cpp
-  for(entt::meta_any element: view) {
+  for(std::pair<entt::meta_any, entt::meta_any> element: view) {
       // ...
   }
   ```
 
   In all cases, given an underlying container of type `C`, the returned element
-  contains an object of type `C::value_type` which therefore depends on the
-  actual container.<br/>
+  is a key-value pair where the key has type `C::key_type` and the value has
+  type `C::mapped_type`. Since key-only containers don't have a mapped type,
+  their _value_ is nothing more than an invalid `meta_any` object.<br/>
   All meta iterators are input iterators and don't offer an indirection operator
   on purpose.
 
+  While the accessed key is usually constant in the associative containers and
+  is therefore returned by copy, the value (if any) is wrapped by an instance of
+  `meta_any` that directly refers to the actual element. Modifying it will then
+  directly modify the element inside the container.
+
 * The `insert` member function can be used to add elements to the container. It
   accepts two arguments, respectively the key and the value to be inserted:
 

+ 140 - 25
src/entt/meta/meta.hpp

@@ -22,7 +22,6 @@ namespace entt {
 
 class meta_type;
 class meta_any;
-class meta_iterator;
 
 
 /*! @brief Proxy object for sequence containers. */
@@ -30,6 +29,8 @@ class meta_sequence_container {
     template<typename>
     struct meta_sequence_container_proxy;
 
+    class meta_iterator;
+
 public:
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
@@ -72,6 +73,8 @@ class meta_associative_container {
     template<typename>
     struct meta_associative_container_proxy;
 
+    class meta_iterator;
+
 public:
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
@@ -1486,8 +1489,11 @@ private:
 }
 
 
-/*! @brief Opaque iterator for meta containers. */
-class meta_iterator {
+/*! @brief Opaque iterator for meta sequence containers. */
+class meta_sequence_container::meta_iterator {
+    /*! @brief A meta sequence container can access the underlying iterator. */
+    friend class meta_sequence_container;
+
     template<typename Type>
     static void incr(meta_any any) {
         ++any.cast<typename Type::iterator>();
@@ -1518,7 +1524,7 @@ public:
     meta_iterator() ENTT_NOEXCEPT
         : next_fn{nullptr},
           get_fn{nullptr},
-          it{}
+          handle{}
     {}
 
     /**
@@ -1530,18 +1536,18 @@ public:
     meta_iterator(std::in_place_type_t<Type>, typename Type::iterator iter)
         : next_fn{&incr<Type>},
           get_fn{&deref<Type>},
-          it{std::move(iter)}
+          handle{std::move(iter)}
     {}
 
     /*! @brief Pre-increment operator. @return This iterator. */
     meta_iterator & operator++() ENTT_NOEXCEPT {
-        return next_fn(it.ref()), *this;
+        return next_fn(handle.ref()), *this;
     }
 
     /*! @brief Post-increment operator. @return This iterator. */
     meta_iterator operator++(int) ENTT_NOEXCEPT {
         meta_iterator orig = *this;
-        return next_fn(it.ref()), orig;
+        return next_fn(handle.ref()), orig;
     }
 
     /**
@@ -1551,7 +1557,7 @@ public:
      * otherwise.
      */
     [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT {
-        return it == other.it;
+        return handle == other.handle;
     }
 
     /**
@@ -1569,15 +1575,7 @@ public:
      * @return The element to which the meta pointer points.
      */
     [[nodiscard]] reference operator*() const {
-        return get_fn(it.ref());
-    }
-
-    /**
-     * @brief Returns a handle to the underlying iterator.
-     * @return The actual iterator, properly wrapped.
-     */
-    [[nodiscard]] meta_any handle() const ENTT_NOEXCEPT {
-        return it.ref();
+        return get_fn(handle.ref());
     }
 
     /**
@@ -1585,13 +1583,13 @@ public:
      * @return False if the iterator is invalid, true otherwise.
      */
     [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
-        return static_cast<bool>(it);
+        return static_cast<bool>(handle);
     }
 
 private:
     void(* next_fn)(meta_any);
     meta_any(* get_fn)(meta_any);
-    entt::meta_any it;
+    entt::meta_any handle;
 };
 
 
@@ -1625,14 +1623,14 @@ struct meta_sequence_container::meta_sequence_container_proxy {
         std::pair<typename meta_sequence_container_traits<Type>::iterator, bool> ret{{}, false};
 
         if(const auto *value = any.try_cast<typename meta_sequence_container_traits<Type>::value_type>(); value) {
-            ret = meta_sequence_container_traits<Type>::insert(*static_cast<Type *>(container), it.handle().cast<typename meta_sequence_container_traits<Type>::iterator>(), *value);
+            ret = meta_sequence_container_traits<Type>::insert(*static_cast<Type *>(container), it.handle.cast<typename meta_sequence_container_traits<Type>::iterator>(), *value);
         }
 
         return {iterator{std::in_place_type<Type>, std::move(ret.first)}, ret.second};
     }
 
     [[nodiscard]] static std::pair<iterator, bool> erase(void *container, iterator it) {
-        auto ret = meta_sequence_container_traits<Type>::erase(*static_cast<Type *>(container), it.handle().cast<typename meta_sequence_container_traits<Type>::iterator>());
+        auto ret = meta_sequence_container_traits<Type>::erase(*static_cast<Type *>(container), it.handle.cast<typename meta_sequence_container_traits<Type>::iterator>());
         return {iterator{std::in_place_type<Type>, std::move(ret.first)}, ret.second};
     }
 
@@ -1778,6 +1776,123 @@ inline std::pair<meta_sequence_container::iterator, bool> meta_sequence_containe
 }
 
 
+/*! @brief Opaque iterator for meta associative containers. */
+class meta_associative_container::meta_iterator {
+    template<typename Type>
+    static void incr(meta_any any) {
+        ++any.cast<typename Type::iterator>();
+    }
+
+    template<typename Type>
+    [[nodiscard]] static meta_any key(meta_any any) {
+        if constexpr(is_key_only_meta_associative_container_v<Type>) {
+            return *any.cast<typename Type::iterator>();
+        } else {
+            return any.cast<typename Type::iterator>()->first;
+        }
+    }
+
+    template<typename Type>
+    [[nodiscard]] static meta_any value(meta_any any) {
+        if constexpr(is_key_only_meta_associative_container_v<Type>) {
+            return meta_any{};
+        } else {
+            if constexpr(std::is_const_v<std::remove_reference_t<decltype(std::declval<typename Type::iterator>()->second)>>) {
+                return any.cast<typename Type::iterator>().second;
+            } else {
+                return std::ref(any.cast<typename Type::iterator>()->second);
+            }
+        }
+    }
+
+public:
+    /*! @brief Signed integer type. */
+    using difference_type = std::ptrdiff_t;
+    /*! @brief Type of elements returned by the iterator. */
+    using value_type = std::pair<meta_any, meta_any>;
+    /*! @brief Pointer type, `void` on purpose. */
+    using pointer = void;
+    /*! @brief Reference type, it is **not** an actual reference. */
+    using reference = value_type;
+    /*! @brief Iterator category. */
+    using iterator_category = std::input_iterator_tag;
+
+    /*! @brief Default constructor. */
+    meta_iterator() ENTT_NOEXCEPT
+        : next_fn{nullptr},
+          key_fn{nullptr},
+          value_fn{nullptr},
+          handle{}
+    {}
+
+    /**
+     * @brief Constructs a meta iterator from a given iterator.
+     * @tparam Type Type of container to which the iterator belongs.
+     * @param iter The actual iterator with which to build the meta iterator.
+     */
+    template<typename Type>
+    meta_iterator(std::in_place_type_t<Type>, typename Type::iterator iter)
+        : next_fn{&incr<Type>},
+          key_fn{&key<Type>},
+          value_fn{&value<Type>},
+          handle{std::move(iter)}
+    {}
+
+    /*! @brief Pre-increment operator. @return This iterator. */
+    meta_iterator & operator++() ENTT_NOEXCEPT {
+        return next_fn(handle.ref()), *this;
+    }
+
+    /*! @brief Post-increment operator. @return This iterator. */
+    meta_iterator operator++(int) ENTT_NOEXCEPT {
+        meta_iterator orig = *this;
+        return next_fn(handle.ref()), orig;
+    }
+
+    /**
+     * @brief Checks if two meta iterators refer to the same element.
+     * @param other The meta iterator with which to compare.
+     * @return True if the two meta iterators refer to the same element, false
+     * otherwise.
+     */
+    [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT {
+        return handle == other.handle;
+    }
+
+    /**
+     * @brief Checks if two meta iterators refer to the same element.
+     * @param other The meta iterator with which to compare.
+     * @return False if the two meta iterators refer to the same element, true
+     * otherwise.
+     */
+    [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT {
+        return !(*this == other);
+    }
+
+    /**
+     * @brief Indirection operator.
+     * @return The element to which the meta pointer points.
+     */
+    [[nodiscard]] reference operator*() const {
+        return { key_fn(handle.ref()), value_fn(handle.ref()) };
+    }
+
+    /**
+     * @brief Returns false if an iterator is invalid, true otherwise.
+     * @return False if the iterator is invalid, true otherwise.
+     */
+    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+        return static_cast<bool>(handle);
+    }
+
+private:
+    void(* next_fn)(meta_any);
+    meta_any(* key_fn)(meta_any);
+    meta_any(* value_fn)(meta_any);
+    entt::meta_any handle;
+};
+
+
 template<typename Type>
 struct meta_associative_container::meta_associative_container_proxy {
     [[nodiscard]] static meta_type key_type() {
@@ -1786,9 +1901,9 @@ struct meta_associative_container::meta_associative_container_proxy {
 
     [[nodiscard]] static meta_type mapped_type() {
         if constexpr(is_key_only_meta_associative_container_v<Type>) {
-            return internal::meta_info<typename meta_associative_container_traits<Type>::mapped_type>::resolve();
-        } else {
             return meta_type{};
+        } else {
+            return internal::meta_info<typename meta_associative_container_traits<Type>::mapped_type>::resolve();
         }
     }
 
@@ -1817,11 +1932,11 @@ struct meta_associative_container::meta_associative_container_proxy {
 
         if(const auto *k_ptr = key.try_cast<typename meta_associative_container_traits<Type>::key_type>(); k_ptr) {
             if constexpr(is_key_only_meta_associative_container_v<Type>) {
+                ret = meta_associative_container_traits<Type>::insert(*static_cast<Type *>(container), *k_ptr);
+            } else {
                 if(auto *m_ptr = value.try_cast<typename meta_associative_container_traits<Type>::mapped_type>(); m_ptr) {
                     ret = meta_associative_container_traits<Type>::insert(*static_cast<Type *>(container), *k_ptr, *m_ptr);
                 }
-            } else {
-                ret = meta_associative_container_traits<Type>::insert(*static_cast<Type *>(container), *k_ptr);
             }
         }
 

+ 2 - 2
src/entt/meta/type_traits.hpp

@@ -78,13 +78,13 @@ inline constexpr auto has_meta_associative_container_traits_v = has_meta_associa
  * @tparam Type Potentially key-only meta associative container type.
  */
 template<typename, typename = void>
-struct is_key_only_meta_associative_container: std::false_type {};
+struct is_key_only_meta_associative_container: std::true_type {};
 
 
 /*! @copydoc is_key_only_meta_associative_container */
 template<typename Type>
 struct is_key_only_meta_associative_container<Type, std::void_t<typename meta_associative_container_traits<Type>::mapped_type>>
-        : std::true_type
+        : std::false_type
 {};
 
 

+ 15 - 14
test/entt/meta/meta_container.cpp

@@ -137,15 +137,15 @@ TEST(MetaAssociativeContainer, StdMap) {
     ASSERT_TRUE(first != last);
 
     ASSERT_NE(first, last);
-    ASSERT_EQ(((*(first++)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{2, 'c'}));
-    ASSERT_EQ(((*(++first++)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{4, 'e'}));
+    ASSERT_EQ((*(first++)).first.cast<int>(), 2);
+    ASSERT_EQ((*(++first)).second.cast<char>(), 'e');
     ASSERT_NE(first++, last);
     ASSERT_EQ(first, last);
 
     ASSERT_TRUE(first == last);
     ASSERT_FALSE(first != last);
 
-    ASSERT_EQ(((*view.find(3)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{3, 'd'}));
+    ASSERT_EQ((*view.find(3)).second.cast<char>(), 'd');
 
     ASSERT_FALSE(view.insert('a', 'a'));
     ASSERT_FALSE(view.insert(1, 1));
@@ -154,16 +154,16 @@ TEST(MetaAssociativeContainer, StdMap) {
     ASSERT_TRUE(view.insert(1, 'b'));
 
     ASSERT_EQ(view.size(), 5u);
-    ASSERT_EQ(((*view.find(0)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{0, 'a'}));
-    ASSERT_EQ(((*view.find(1)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{1, 'b'}));
+    ASSERT_EQ((*view.find(0)).second.cast<char>(), 'a');
+    ASSERT_EQ((*view.find(1)).second.cast<char>(), 'b');
 
     ASSERT_TRUE(view.erase(0));
     ASSERT_EQ(view.size(), 4u);
     ASSERT_EQ(view.find(0), view.end());
 
-    (*view.find(1)).cast<std::pair<const int, char>>().second = 'f';
+    (*view.find(1)).second.cast<char>() = 'f';
 
-    ASSERT_EQ(((*view.find(1)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{1, 'f'}));
+    ASSERT_EQ((*view.find(1)).second.cast<char>(), 'f');
 }
 
 TEST(MetaAssociativeContainer, StdSet) {
@@ -182,15 +182,16 @@ TEST(MetaAssociativeContainer, StdSet) {
     ASSERT_TRUE(first != last);
 
     ASSERT_NE(first, last);
-    ASSERT_EQ((*(first++)).cast<int>(), 2);
-    ASSERT_EQ((*(++first++)).cast<int>(), 4);
+    ASSERT_FALSE((*first).second);
+    ASSERT_EQ((*(first++)).first.cast<int>(), 2);
+    ASSERT_EQ((*(++first)).first.cast<int>(), 4);
     ASSERT_NE(first++, last);
     ASSERT_EQ(first, last);
 
     ASSERT_TRUE(first == last);
     ASSERT_FALSE(first != last);
 
-    ASSERT_EQ((*view.find(3)).cast<int>(), 3);
+    ASSERT_EQ((*view.find(3)).first.cast<int>(), 3);
 
     ASSERT_FALSE(view.insert('0'));
 
@@ -198,14 +199,14 @@ TEST(MetaAssociativeContainer, StdSet) {
     ASSERT_TRUE(view.insert(1));
 
     ASSERT_EQ(view.size(), 5u);
-    ASSERT_EQ((*view.find(0)).cast<int>(), 0);
-    ASSERT_EQ((*view.find(1)).cast<int>(), 1);
+    ASSERT_EQ((*view.find(0)).first.cast<int>(), 0);
+    ASSERT_EQ((*view.find(1)).first.cast<int>(), 1);
 
     ASSERT_TRUE(view.erase(0));
     ASSERT_EQ(view.size(), 4u);
     ASSERT_EQ(view.find(0), view.end());
 
-    (*view.find(1)).cast<int>() = 42;
+    (*view.find(1)).first.cast<int>() = 42;
 
-    ASSERT_EQ((*view.find(1)).cast<int>(), 1);
+    ASSERT_EQ((*view.find(1)).first.cast<int>(), 1);
 }