Procházet zdrojové kódy

meta: containers support (close #499)

Michele Caini před 5 roky
rodič
revize
3400b32015

+ 4 - 0
src/entt/meta/internal.hpp

@@ -116,6 +116,8 @@ struct meta_type_node {
     const bool is_function_pointer;
     const bool is_member_object_pointer;
     const bool is_member_function_pointer;
+    const bool is_sequence_container;
+    const bool is_associative_container;
     const size_type rank;
     size_type(* const extent)(size_type);
     bool(* const compare)(const void *, const void *);
@@ -257,6 +259,8 @@ public:
             std::is_pointer_v<Type> && std::is_function_v<std::remove_pointer_t<Type>>,
             std::is_member_object_pointer_v<Type>,
             std::is_member_function_pointer_v<Type>,
+            is_sequence_container_v<Type>,
+            is_associative_container_v<Type>,
             std::rank_v<Type>,
             [](meta_type_node::size_type dim) {
                 return extent(dim, std::make_index_sequence<std::rank_v<Type>>{});

+ 464 - 58
src/entt/meta/meta.hpp

@@ -20,10 +20,60 @@ namespace entt {
 
 
 class meta_type;
+class meta_any;
+
+
+/*! @brief Proxy object for containers of any type. */
+class meta_container {
+    /*! @brief A meta_any is allowed to create proxies. */
+    friend class meta_any;
+
+    struct meta_iterator;
+
+    struct meta_view {
+        [[nodiscard]] virtual std::size_t size(void *) const ENTT_NOEXCEPT = 0;
+        [[nodiscard]] virtual bool insert(void *, meta_any, meta_any) = 0;
+        [[nodiscard]] virtual bool erase(void *, meta_any) = 0;
+        [[nodiscard]] virtual meta_any begin(void *) const ENTT_NOEXCEPT = 0;
+        [[nodiscard]] virtual meta_any end(void *) const ENTT_NOEXCEPT = 0;
+        [[nodiscard]] virtual meta_any find(void *, meta_any) const ENTT_NOEXCEPT = 0;
+        [[nodiscard]] virtual meta_any value(meta_any) const ENTT_NOEXCEPT = 0;
+        virtual void incr(meta_any) const ENTT_NOEXCEPT = 0;
+    };
+
+    meta_container(meta_view *impl, void *container)
+        : view{impl},
+          instance{container}
+    {}
+
+public:
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+    /*! @brief Input iterator type. */
+    using iterator = meta_iterator;
+
+    /*! @brief Default constructor. */
+    meta_container()
+        : view{},
+          instance{}
+    {}
+
+    [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT;
+    inline bool insert(meta_any, meta_any);
+    inline bool erase(meta_any);
+    [[nodiscard]] inline iterator begin();
+    [[nodiscard]] inline iterator end();
+    [[nodiscard]] inline iterator operator[](meta_any pos_or_key);
+    [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT;
+
+private:
+    meta_view *view;
+    void *instance;
+};
 
 
 /**
- * @brief Opaque container for values of any type.
+ * @brief Opaque wrapper for values of any type.
  *
  * This class uses a technique called small buffer optimization (SBO) to get rid
  * of memory allocations if possible. This should improve overall performance.
@@ -86,12 +136,158 @@ class meta_any {
         }
     };
 
-    meta_any(const internal::meta_type_node *curr, void *ref) ENTT_NOEXCEPT
-        : meta_any{}
-    {
-        node = curr;
-        instance = ref;
-    }
+    template<typename, typename = void>
+    struct container_view {
+        [[nodiscard]] static meta_container::meta_view * instance() {
+            return nullptr;
+        }
+    };
+
+    template<typename Type>
+    struct container_view<Type, std::enable_if_t<is_sequence_container_v<Type>>>: meta_container::meta_view {
+        [[nodiscard]] static meta_container::meta_view * instance() {
+            static container_view common{};
+            return &common;
+        }
+
+        [[nodiscard]] std::size_t size(void *container) const ENTT_NOEXCEPT override {
+            return static_cast<Type *>(container)->size();
+        }
+
+        [[nodiscard]] bool insert(void *container, meta_any it, meta_any value) override {
+            bool ret = false;
+
+            if constexpr(is_dynamic_sequence_container_v<Type>) {
+                if(auto *iter = it.try_cast<typename Type::iterator>(); iter) {
+                    if(const auto *curr = value.try_cast<typename Type::value_type>(); curr) {
+                        *iter = static_cast<Type *>(container)->insert(*iter, *curr);
+                        ret = true;
+                    }
+                }
+            }
+
+            return ret;
+        }
+
+        [[nodiscard]] bool erase(void *container, meta_any it) override {
+            bool ret = false;
+
+            if constexpr(is_dynamic_sequence_container_v<Type>) {
+                if(auto *iter = it.try_cast<typename Type::iterator>(); iter) {
+                    *iter = static_cast<Type *>(container)->erase(*iter);
+                    ret = true;
+                }
+            }
+
+            return ret;
+        }
+
+        [[nodiscard]] meta_any begin(void *container) const ENTT_NOEXCEPT override {
+            return static_cast<Type *>(container)->begin();
+        }
+
+        [[nodiscard]] meta_any end(void *container) const ENTT_NOEXCEPT override {
+            return static_cast<Type *>(container)->end();
+        }
+
+        [[nodiscard]] meta_any value(meta_any it) const ENTT_NOEXCEPT override {
+            return std::ref(*it.cast<typename Type::iterator>());
+        }
+
+        [[nodiscard]] meta_any find(void *container, meta_any idx) const ENTT_NOEXCEPT override {
+            meta_any any{};
+
+            if(const auto *curr = idx.try_cast<std::size_t>(); curr) {
+                any = std::next(static_cast<Type *>(container)->begin(), *curr);
+            }
+
+            return any;
+        }
+
+        void incr(meta_any it) const ENTT_NOEXCEPT override {
+            ++it.cast<typename Type::iterator>();
+        }
+    };
+
+    template<typename Type>
+    struct container_view<Type, std::enable_if_t<is_associative_container_v<Type>>>: meta_container::meta_view {
+        [[nodiscard]] static meta_container::meta_view * instance() {
+            static container_view common{};
+            return &common;
+        }
+
+        [[nodiscard]] std::size_t size(void *container) const ENTT_NOEXCEPT override {
+            return static_cast<Type *>(container)->size();
+        }
+
+        [[nodiscard]] bool insert(void *container, meta_any key, meta_any value) override {
+            bool ret = false;
+
+            if constexpr(is_key_only_associative_container_v<Type>) {
+                if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
+                    static_cast<Type *>(container)->insert(*curr);
+                    ret = true;
+                }
+            } else {
+                if(const auto *k_curr = key.try_cast<typename Type::key_type>(); k_curr) {
+                    if(const auto *v_curr = value.try_cast<typename Type::mapped_type>(); v_curr) {
+                        static_cast<Type *>(container)->insert(std::make_pair(*k_curr, *v_curr));
+                        ret = true;
+                    }
+                }
+            }
+
+            return ret;
+        }
+
+        [[nodiscard]] bool erase(void *container, meta_any key) override {
+            bool ret = false;
+
+            if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
+                static_cast<Type *>(container)->erase(*curr);
+                ret = true;
+            }
+
+            return ret;
+        }
+
+        [[nodiscard]] meta_any begin(void *container) const ENTT_NOEXCEPT override {
+            return static_cast<Type *>(container)->begin();
+        }
+
+        [[nodiscard]] meta_any end(void *container) const ENTT_NOEXCEPT override {
+            return static_cast<Type *>(container)->end();
+        }
+
+        [[nodiscard]] meta_any value(meta_any it) const ENTT_NOEXCEPT override {
+            if constexpr(is_key_only_associative_container_v<Type>) {
+                return *it.cast<typename Type::iterator>();
+            } else {
+                return std::ref(*it.cast<typename Type::iterator>());
+            }
+        }
+
+        [[nodiscard]] meta_any find(void *container, meta_any key) const ENTT_NOEXCEPT override {
+            meta_any any{};
+
+            if constexpr(is_key_only_associative_container_v<Type>) {
+                if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
+                    auto *cont = static_cast<Type *>(container);
+                    any = std::find(cont->begin(), cont->end(), *curr);
+                }
+            } else {
+                if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
+                    any = static_cast<Type *>(container)->find(*curr);
+                }
+            }
+
+            return any;
+        }
+
+        void incr(meta_any it) const ENTT_NOEXCEPT override {
+            it.cast<typename Type::iterator>().operator++();
+        }
+    };
 
 public:
     /*! @brief Default constructor. */
@@ -101,12 +297,13 @@ public:
           node{},
           destroy_fn{},
           copy_fn{},
-          steal_fn{}
+          steal_fn{},
+          cview{}
     {}
 
     /**
      * @brief Constructs a meta any by directly initializing the new object.
-     * @tparam Type Type of object to use to initialize the container.
+     * @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.
      */
@@ -121,23 +318,28 @@ public:
             destroy_fn = &type_traits<Type>::destroy;
             copy_fn = &type_traits<Type>::copy;
             steal_fn = &type_traits<Type>::steal;
+            cview = container_view<Type>::instance();
         }
     }
 
     /**
      * @brief Constructs a meta any that holds an unmanaged object.
-     * @tparam Type Type of object to use to initialize the container.
-     * @param value An instance of an object to use to initialize the container.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
      */
     template<typename Type>
     meta_any(std::reference_wrapper<Type> value)
-        : meta_any{internal::meta_info<Type>::resolve(), &value.get()}
-    {}
+        : meta_any{}
+    {
+        node = internal::meta_info<Type>::resolve();
+        instance = &value.get();
+        cview = container_view<Type>::instance();
+    }
 
     /**
      * @brief Constructs a meta any from a given value.
-     * @tparam Type Type of object to use to initialize the container.
-     * @param value An instance of an object to use to initialize the container.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
      */
     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>>>
     meta_any(Type &&value)
@@ -156,6 +358,7 @@ public:
         destroy_fn = other.destroy_fn;
         copy_fn = other.copy_fn;
         steal_fn = other.steal_fn;
+        cview = other.cview;
     }
 
     /**
@@ -177,8 +380,8 @@ public:
 
     /**
      * @brief Assignment operator.
-     * @tparam Type Type of object to use to initialize the container.
-     * @param value An instance of an object to use to initialize the container.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
      * @return This meta any object.
      */
     template<typename Type>
@@ -309,9 +512,8 @@ public:
     }
 
     /**
-     * @brief Replaces the contained object by initializing a new instance
-     * directly.
-     * @tparam Type Type of object to use to initialize the container.
+     * @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.
      */
@@ -325,7 +527,19 @@ public:
      * @return A meta any that shares a reference to an unmanaged object.
      */
     [[nodiscard]] meta_any ref() const ENTT_NOEXCEPT {
-        return meta_any{node, instance};
+        meta_any alias{};
+        alias.node = node;
+        alias.instance = instance;
+        alias.cview = cview;
+        return alias;
+    }
+
+    /**
+     * @brief Returns a container view.
+     * @return A container view for the underlying object.
+     */
+    [[nodiscard]] meta_container view() const ENTT_NOEXCEPT {
+        return { cview, instance };
     }
 
     /**
@@ -337,18 +551,17 @@ public:
     }
 
     /**
-     * @brief Returns false if a container is empty, true otherwise.
-     * @return False if the container is empty, true otherwise.
+     * @brief Returns false if a wrapper is empty, true otherwise.
+     * @return False if the wrapper is empty, true otherwise.
      */
     [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
         return !(node == nullptr);
     }
 
     /**
-     * @brief Checks if two containers differ in their content.
-     * @param other Container with which to compare.
-     * @return False if the two containers differ in their content, true
-     * otherwise.
+     * @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.
      */
     [[nodiscard]] bool operator==(const meta_any &other) const {
         return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id && node->compare(instance, other.instance));
@@ -377,6 +590,7 @@ public:
         std::swap(lhs.destroy_fn, rhs.destroy_fn);
         std::swap(lhs.copy_fn, rhs.copy_fn);
         std::swap(lhs.steal_fn, rhs.steal_fn);
+        std::swap(lhs.cview, rhs.cview);
     }
 
 private:
@@ -386,20 +600,196 @@ private:
     destroy_fn_type *destroy_fn;
     copy_fn_type *copy_fn;
     steal_fn_type *steal_fn;
+    meta_container::meta_view *cview;
 };
 
 
 /**
- * @brief Checks if two containers differ in their content.
+ * @brief Checks if two wrappers differ in their content.
  * @param lhs A meta any object, either empty or not.
  * @param rhs A meta any object, either empty or not.
- * @return True if the two containers differ in their content, false otherwise.
+ * @return True if the two wrappers differ in their content, false otherwise.
  */
 [[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT {
     return !(lhs == rhs);
 }
 
 
+/*! @brief Opaque iterator for meta containers. */
+struct meta_container::meta_iterator {
+    /*! @brief Signed integer type. */
+    using difference_type = std::ptrdiff_t;
+    /*! @brief Type of elements returned by the iterator. */
+    using value_type = 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 = default;
+
+    /**
+     * @brief Constructs a meta iterator that wraps an actual iterator.
+     * @param ref A proxy object that _knows_ how to use the wrapped iterator.
+     * @param iter The actual iterator, properly wrapped.
+     */
+    meta_iterator(meta_container::meta_view *ref, meta_any iter)
+        : view{ref},
+          it{std::move(iter)}
+    {}
+
+    /*! @brief Pre-increment operator. @return This iterator. */
+    meta_iterator & operator++() ENTT_NOEXCEPT {
+        return view->incr(handle()), *this;
+    }
+
+    /*! @brief Post-increment operator. @return This iterator. */
+    meta_iterator operator++(int) ENTT_NOEXCEPT {
+        iterator orig = *this;
+        return view->incr(handle()), 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 it == other.it;
+    }
+
+    /**
+     * @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 view->value(handle());
+    }
+
+    /**
+     * @brief Returns a handle to the underlying iterator.
+     * @return The actual iterator, properly wrapped.
+     */
+    [[nodiscard]] meta_any handle() const ENTT_NOEXCEPT {
+        return *it;
+    }
+
+    /**
+     * @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>(it);
+    }
+
+private:
+    meta_container::meta_view *view;
+    entt::meta_any it;
+};
+
+
+/**
+ * @brief Returns the number of elements.
+ * @return Number of elements.
+ */
+[[nodiscard]] meta_container::size_type meta_container::size() const ENTT_NOEXCEPT {
+    return view->size(instance);
+}
+
+
+/**
+ * @brief Inserts an element in the container.
+ *
+ * In case of sequence containers, the first parameter must be a valid iterator
+ * handle. In case of associative containers, the type of the key must be such
+ * that a cast or conversion to the key type of the container is possible.<br/>
+ * The type of the value must be such that a cast or conversion to the value
+ * type of the container is possible.
+ *
+ * @param it_or_key A valid iterator handle in case of sequence containers, a
+ * key in case of associative containers.
+ * @param value The element to insert in the container.
+ * @return True in case of success, false otherwise.
+ */
+inline bool meta_container::insert(meta_any it_or_key, meta_any value = {}) {
+    return view->insert(instance, std::move(it_or_key), std::move(value));
+}
+
+
+/**
+ * @brief Erases an element from the container.
+ *
+ * In case of sequence containers, the parameter must be a valid iterator
+ * handle. In case of associative containers, the type of the key must be such
+ * that a cast or conversion to the key type of the container is possible.
+ *
+ * @param it_or_key A valid iterator handle in case of sequence containers, a
+ * key in case of associative containers.
+ * @return True in case of success, false otherwise.
+ */
+inline bool meta_container::erase(meta_any it_or_key) {
+    return view->erase(instance, std::move(it_or_key));
+}
+
+
+/**
+ * @brief Returns an iterator to the first element of the container.
+ * @return An iterator to the first element of the container.
+ */
+[[nodiscard]] inline meta_container::iterator meta_container::begin() {
+    return {view, view->begin(instance)};
+}
+
+
+/**
+ * @brief Returns an iterator that is past the last element of the container.
+ * @return An iterator that is past the last element of the container.
+ */
+[[nodiscard]] inline meta_container::iterator meta_container::end() {
+    return {view, view->end(instance)};
+}
+
+
+/**
+ * @brief Returns an iterator to the required element.
+ *
+ * In case of sequence containers, the parameter must be a valid position (no
+ * bounds checking is performed) and such that a cast or conversion to size_type
+ * is possible. In case of associative containers, the type of the key must be
+ * such that a cast or conversion to the key type of the container is possible.
+ *
+ * @param pos_or_key A valid position in case of sequence containers, a key in
+ * case of associative containers.
+ * @return An iterator to the required element.
+ */
+[[nodiscard]] inline meta_container::iterator meta_container::operator[](meta_any pos_or_key) {
+    return {view, view->find(instance, std::move(pos_or_key))};
+}
+
+
+/**
+ * @brief Returns true if a meta container is valid, false otherwise.
+ * @return True if the meta container is valid, false otherwise.
+ */
+[[nodiscard]] meta_container::operator bool() const ENTT_NOEXCEPT {
+    return view;
+}
+
+
 /**
  * @brief Opaque pointers to instances of any type.
  *
@@ -415,8 +805,8 @@ struct meta_handle {
 
     /**
      * @brief Creates a handle that points to an unmanaged object.
-     * @tparam Type Type of object to use to initialize the container.
-     * @param value An instance of an object to use to initialize the container.
+     * @tparam Type Type of object to use to initialize the handle.
+     * @param value An instance of an object to use to initialize the handle.
      */
     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_handle>>>
     meta_handle(Type &&value) ENTT_NOEXCEPT
@@ -440,7 +830,7 @@ private:
 };
 
 
-/*! @brief Opaque container for meta properties of any type. */
+/*! @brief Opaque wrapper for meta properties of any type. */
 struct meta_prop {
     /*! @brief Node type. */
     using node_type = internal::meta_prop_node;
@@ -482,7 +872,7 @@ private:
 };
 
 
-/*! @brief Opaque container for meta base classes. */
+/*! @brief Opaque wrapper for meta base classes. */
 struct meta_base {
     /*! @brief Node type. */
     using node_type = internal::meta_base_node;
@@ -523,7 +913,7 @@ private:
 };
 
 
-/*! @brief Opaque container for meta conversion functions. */
+/*! @brief Opaque wrapper for meta conversion functions. */
 struct meta_conv {
     /*! @brief Node type. */
     using node_type = internal::meta_conv_node;
@@ -561,7 +951,7 @@ private:
 };
 
 
-/*! @brief Opaque container for meta constructors. */
+/*! @brief Opaque wrapper for meta constructors. */
 struct meta_ctor {
     /*! @brief Node type. */
     using node_type = internal::meta_ctor_node;
@@ -596,7 +986,7 @@ struct meta_ctor {
      *
      * To create a valid instance, the parameters must be such that a cast or
      * conversion to the required types is possible. Otherwise, an empty and
-     * thus invalid container is returned.
+     * thus invalid wrapper is returned.
      *
      * @tparam Args Types of arguments to use to construct the instance.
      * @param args Parameters to use to construct the instance.
@@ -653,7 +1043,7 @@ private:
 };
 
 
-/*! @brief Opaque container for meta data. */
+/*! @brief Opaque wrapper for meta data. */
 struct meta_data {
     /*! @brief Node type. */
     using node_type = internal::meta_data_node;
@@ -764,7 +1154,7 @@ private:
 };
 
 
-/*! @brief Opaque container for meta functions. */
+/*! @brief Opaque wrapper for meta functions. */
 struct meta_func {
     /*! @brief Node type. */
     using node_type = internal::meta_func_node;
@@ -826,7 +1216,7 @@ struct meta_func {
      *
      * To invoke a meta function, the parameters must be such that a cast or
      * conversion to the required types is possible. Otherwise, an empty and
-     * thus invalid container is returned.<br/>
+     * thus invalid wrapper is returned.<br/>
      * It must be possible to cast the instance to the parent type of the meta
      * function. Otherwise, invoking the underlying function results in an
      * undefined behavior.
@@ -890,7 +1280,7 @@ private:
 };
 
 
-/*! @brief Opaque container for meta types. */
+/*! @brief Opaque wrapper for meta types. */
 class meta_type {
     template<typename... Args, std::size_t... Indexes>
     [[nodiscard]] auto ctor(std::index_sequence<Indexes...>) const {
@@ -933,7 +1323,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to void or not.
+     * @brief Checks whether a type refers to void or not.
      * @return True if the underlying type is void, false otherwise.
      */
     [[nodiscard]] bool is_void() const ENTT_NOEXCEPT {
@@ -941,7 +1331,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to an integral type or not.
+     * @brief Checks whether a type refers to an integral type or not.
      * @return True if the underlying type is an integral type, false otherwise.
      */
     [[nodiscard]] bool is_integral() const ENTT_NOEXCEPT {
@@ -949,8 +1339,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to a floating-point type or
-     * not.
+     * @brief Checks whether a type refers to a floating-point type or not.
      * @return True if the underlying type is a floating-point type, false
      * otherwise.
      */
@@ -959,7 +1348,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to an array type or not.
+     * @brief Checks whether a type refers to an array type or not.
      * @return True if the underlying type is an array type, false otherwise.
      */
     [[nodiscard]] bool is_array() const ENTT_NOEXCEPT {
@@ -967,7 +1356,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to an enum or not.
+     * @brief Checks whether a type refers to an enum or not.
      * @return True if the underlying type is an enum, false otherwise.
      */
     [[nodiscard]] bool is_enum() const ENTT_NOEXCEPT {
@@ -975,7 +1364,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to an union or not.
+     * @brief Checks whether a type refers to an union or not.
      * @return True if the underlying type is an union, false otherwise.
      */
     [[nodiscard]] bool is_union() const ENTT_NOEXCEPT {
@@ -983,7 +1372,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to a class or not.
+     * @brief Checks whether a type refers to a class or not.
      * @return True if the underlying type is a class, false otherwise.
      */
     [[nodiscard]] bool is_class() const ENTT_NOEXCEPT {
@@ -991,7 +1380,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to a pointer or not.
+     * @brief Checks whether a type refers to a pointer or not.
      * @return True if the underlying type is a pointer, false otherwise.
      */
     [[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT {
@@ -999,7 +1388,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to a function pointer or not.
+     * @brief Checks whether a type refers to a function pointer or not.
      * @return True if the underlying type is a function pointer, false
      * otherwise.
      */
@@ -1008,8 +1397,7 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to a pointer to data member
-     * or not.
+     * @brief Checks whether a type refers to a pointer to data member or not.
      * @return True if the underlying type is a pointer to data member, false
      * otherwise.
      */
@@ -1018,8 +1406,8 @@ public:
     }
 
     /**
-     * @brief Indicates whether a meta type refers to a pointer to member
-     * function or not.
+     * @brief Checks whether a type refers to a pointer to member function or
+     * not.
      * @return True if the underlying type is a pointer to member function,
      * false otherwise.
      */
@@ -1028,7 +1416,25 @@ public:
     }
 
     /**
-     * @brief If a meta type refers to an array type, provides the number of
+     * @brief Checks whether a type refers to a sequence container or not.
+     * @return True if the underlying type is a sequence container, false
+     * otherwise.
+     */
+    [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT {
+        return node->is_sequence_container;
+    }
+
+    /**
+     * @brief Checks whether a type refers to an associative container or not.
+     * @return True if the underlying type is an associative container, false
+     * otherwise.
+     */
+    [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT {
+        return node->is_associative_container;
+    }
+
+    /**
+     * @brief If a type refers to an array type, provides the number of
      * dimensions of the array.
      * @return The number of dimensions of the array if the underlying type is
      * an array type, 0 otherwise.
@@ -1038,8 +1444,8 @@ public:
     }
 
     /**
-     * @brief If a meta type refers to an array type, provides the number of
-     * elements along the given dimension of the array.
+     * @brief If a type refers to an array type, provides the number of elements
+     * along the given dimension of the array.
      * @param dim The dimension of which to return the number of elements.
      * @return The number of elements along the given dimension of the array if
      * the underlying type is an array type, 0 otherwise.
@@ -1243,7 +1649,7 @@ public:
      *
      * To create a valid instance, the parameters must be such that a cast or
      * conversion to the required types is possible. Otherwise, an empty and
-     * thus invalid container is returned.
+     * thus invalid wrapper is returned.
      *
      * @tparam Args Types of arguments to use to construct the instance.
      * @param args Parameters to use to construct the instance.

+ 1 - 0
test/CMakeLists.txt

@@ -179,6 +179,7 @@ SETUP_BASIC_TEST(locator entt/locator/locator.cpp)
 
 SETUP_BASIC_TEST(meta_any entt/meta/meta_any.cpp)
 SETUP_BASIC_TEST(meta_base entt/meta/meta_base.cpp)
+SETUP_BASIC_TEST(meta_container entt/meta/meta_container.cpp)
 SETUP_BASIC_TEST(meta_conv entt/meta/meta_conv.cpp)
 SETUP_BASIC_TEST(meta_ctor entt/meta/meta_ctor.cpp)
 SETUP_BASIC_TEST(meta_data entt/meta/meta_data.cpp)

+ 193 - 0
test/entt/meta/meta_container.cpp

@@ -0,0 +1,193 @@
+#include <array>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+#include <gtest/gtest.h>
+#include <entt/core/hashed_string.hpp>
+#include <entt/meta/factory.hpp>
+#include <entt/meta/meta.hpp>
+#include <entt/meta/resolve.hpp>
+
+TEST(MetaContainer, Empty) {
+    entt::meta_container container{};
+
+    ASSERT_FALSE(container);
+
+    entt::meta_any any{std::vector<int>{}};
+    container = any.view();
+
+    ASSERT_TRUE(container);
+}
+
+TEST(MetaContainer, DynamicSequenceContainer) {
+    std::vector<int> vec{2, 3, 4};
+    entt::meta_any any{std::ref(vec)};
+
+    auto view = any.view();
+
+    ASSERT_TRUE(view);
+    ASSERT_EQ(view.size(), 3u);
+
+    auto first = view.begin();
+    const auto last = view.end();
+
+    ASSERT_FALSE(first == last);
+    ASSERT_TRUE(first != last);
+
+    ASSERT_NE(first, last);
+    ASSERT_EQ((*(first++)).cast<int>(), 2);
+    ASSERT_EQ((*(++first)).cast<int>(), 4);
+    ASSERT_NE(first++, last);
+    ASSERT_EQ(first, last);
+
+    ASSERT_TRUE(first == last);
+    ASSERT_FALSE(first != last);
+
+    ASSERT_EQ((*view[std::size_t{1u}]).cast<int>(), 3);
+
+    auto it = view.begin();
+
+    ASSERT_TRUE(view.insert(it.handle(), 0));
+    ASSERT_TRUE(view.insert((++it).handle(), 1));
+
+    ASSERT_EQ(view.size(), 5u);
+    ASSERT_EQ((*view.begin()).cast<int>(), 0);
+    ASSERT_EQ((*++view.begin()).cast<int>(), 1);
+
+    it = view.begin();
+
+    ASSERT_TRUE(view.erase(it.handle()));
+    ASSERT_EQ(view.size(), 4u);
+    ASSERT_EQ((*it).cast<int>(), 1);
+
+    (*view[std::size_t{}]).cast<int>() = 5;
+
+    ASSERT_EQ((*view.begin()).cast<int>(), 5);
+}
+
+TEST(MetaContainer, FixedSizeSequenceContainer) {
+    std::array<int, 3> arr{2, 3, 4};
+    entt::meta_any any{std::ref(arr)};
+
+    auto view = any.view();
+
+    ASSERT_TRUE(view);
+    ASSERT_EQ(view.size(), 3u);
+
+    auto first = view.begin();
+    const auto last = view.end();
+
+    ASSERT_FALSE(first == last);
+    ASSERT_TRUE(first != last);
+
+    ASSERT_NE(first, last);
+    ASSERT_EQ((*(first++)).cast<int>(), 2);
+    ASSERT_EQ((*(++first)).cast<int>(), 4);
+    ASSERT_NE(first++, last);
+    ASSERT_EQ(first, last);
+
+    ASSERT_TRUE(first == last);
+    ASSERT_FALSE(first != last);
+
+    ASSERT_EQ((*view[std::size_t{1u}]).cast<int>(), 3);
+
+    auto it = view.begin();
+
+    ASSERT_FALSE(view.insert(it.handle(), 0));
+    ASSERT_FALSE(view.insert((++it).handle(), 1));
+
+    ASSERT_EQ(view.size(), 3u);
+    ASSERT_EQ((*view.begin()).cast<int>(), 2);
+    ASSERT_EQ((*++view.begin()).cast<int>(), 3);
+
+    it = view.begin();
+
+    ASSERT_FALSE(view.erase(it.handle()));
+    ASSERT_EQ(view.size(), 3u);
+    ASSERT_EQ((*it).cast<int>(), 2);
+
+    (*view[std::size_t{}]).cast<int>() = 5;
+
+    ASSERT_EQ((*view.begin()).cast<int>(), 5);
+}
+
+TEST(MetaContainer, KeyValueAssociativeContainer) {
+    std::map<int, char> map{{2, 'c'}, {3, 'd'}, {4, 'e'}};
+    entt::meta_any any{std::ref(map)};
+
+    auto view = any.view();
+
+    ASSERT_TRUE(view);
+    ASSERT_EQ(view.size(), 3u);
+
+    auto first = view.begin();
+    const auto last = view.end();
+
+    ASSERT_FALSE(first == last);
+    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_NE(first++, last);
+    ASSERT_EQ(first, last);
+
+    ASSERT_TRUE(first == last);
+    ASSERT_FALSE(first != last);
+
+    ASSERT_EQ(((*view[3]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{3, 'd'}));
+
+    ASSERT_TRUE(view.insert(0, 'a'));
+    ASSERT_TRUE(view.insert(1, 'b'));
+
+    ASSERT_EQ(view.size(), 5u);
+    ASSERT_EQ(((*view[0]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{0, 'a'}));
+    ASSERT_EQ(((*view[1]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{1, 'b'}));
+
+    ASSERT_TRUE(view.erase(0));
+    ASSERT_EQ(view.size(), 4u);
+    ASSERT_EQ(view[0], view.end());
+
+    (*view[1]).cast<std::pair<const int, char>>().second = 'f';
+
+    ASSERT_EQ(((*view[1]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{1, 'f'}));
+}
+
+TEST(MetaContainer, KeyOnlyAssociativeContainer) {
+    std::set<int> set{2, 3, 4};
+    entt::meta_any any{std::ref(set)};
+
+    auto view = any.view();
+
+    ASSERT_TRUE(view);
+    ASSERT_EQ(view.size(), 3u);
+
+    auto first = view.begin();
+    const auto last = view.end();
+
+    ASSERT_FALSE(first == last);
+    ASSERT_TRUE(first != last);
+
+    ASSERT_NE(first, last);
+    ASSERT_EQ((*(first++)).cast<int>(), 2);
+    ASSERT_EQ((*(++first++)).cast<int>(), 4);
+    ASSERT_NE(first++, last);
+    ASSERT_EQ(first, last);
+
+    ASSERT_TRUE(first == last);
+    ASSERT_FALSE(first != last);
+
+    ASSERT_NE(view[3], view.end());
+
+    ASSERT_TRUE(view.insert(0, 'a'));
+    ASSERT_TRUE(view.insert(1));
+
+    ASSERT_EQ(view.size(), 5u);
+    ASSERT_NE(view[0], view.end());
+    ASSERT_NE(view[1], view.end());
+
+    ASSERT_TRUE(view.erase(0));
+    ASSERT_EQ(view.size(), 4u);
+    ASSERT_EQ(view[0], view.end());
+}

+ 6 - 0
test/entt/meta/meta_type.cpp

@@ -162,6 +162,12 @@ TEST_F(MetaType, Traits) {
     ASSERT_TRUE(entt::resolve<decltype(&clazz_t::member)>().is_member_function_pointer());
     ASSERT_FALSE(entt::resolve<decltype(&clazz_t::value)>().is_member_function_pointer());
 
+    ASSERT_TRUE(entt::resolve<std::vector<int>>().is_sequence_container());
+    ASSERT_FALSE((entt::resolve<std::map<int, char>>().is_sequence_container()));
+
+    ASSERT_TRUE((entt::resolve<std::map<int, char>>().is_associative_container()));
+    ASSERT_FALSE(entt::resolve<std::vector<int>>().is_associative_container());
+
     ASSERT_EQ(entt::resolve<int>().rank(), 0u);
     ASSERT_EQ(entt::resolve<int[5][3]>().rank(), 2u);
     ASSERT_EQ(entt::resolve<int>().extent(), 0u);