Explorar o código

meta: iterable base types as instances of meta_type

Michele Caini %!s(int64=5) %!d(string=hai) anos
pai
achega
0ec755fccf
Modificáronse 4 ficheiros con 53 adicións e 15 borrados
  1. 5 11
      docs/md/meta.md
  2. 27 0
      src/entt/meta/meta.hpp
  3. 5 4
      src/entt/meta/range.hpp
  4. 16 0
      test/entt/meta/meta_type.cpp

+ 5 - 11
docs/md/meta.md

@@ -309,11 +309,8 @@ The meta objects that compose a meta type are accessed in the following ways:
   auto base = entt::resolve<derived_type>().base("base"_hs);
   ```
 
-  The returned type is `meta_base` and may be invalid if there is no meta base
-  object associated with the given identifier.<br/>
-  Meta bases aren't meant to be used directly, even though they are freely
-  accessible. They expose only a few methods to use to know the meta type of the
-  base class and to convert a raw pointer between types.
+  The returned type is `meta_type` and may be invalid if there is no meta base
+  object associated with the given identifier.
 
 All the objects thus obtained as well as the meta types can be explicitly
 converted to a boolean value to check if they are valid:
@@ -325,7 +322,7 @@ if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
 ```
 
 Furthermore, all them are also returned by specific overloads that provide the
-caller with iterable objects. As an example:
+caller with iterable ranges of top-level elements. As an example:
 
 ```cpp
 for(auto data = entt::resolve<my_type>().data()) {
@@ -345,11 +342,8 @@ member function in its API. Destructors are invoked implicitly by `meta_any`
 behind the scenes and users have not to deal with them explicitly. Furthermore,
 they have no name, cannot be searched and wouldn't have member functions to
 expose anyway.<br/>
-Similarly, conversion functions and base types aren't directly accessible. They
-are used internally by `meta_any` and the meta objects when needed.<br/>
-It wouldn't make sense to give direct access to these elements and to open the
-doors to the possibility of making mistakes. On the other side, the library
-already offers enough ways to use them correctly.
+Similarly, conversion functions aren't directly accessible. They are used
+internally by `meta_any` and the meta objects when needed.
 
 Meta types and meta objects in general contain much more than what is said: a
 plethora of functions in addition to those listed whose purposes and uses go

+ 27 - 0
src/entt/meta/meta.hpp

@@ -1180,6 +1180,8 @@ class meta_type {
 public:
     /*! @brief Node type. */
     using node_type = internal::meta_type_node;
+    /*! @brief Node type. */
+    using base_node_type = internal::meta_base_node;
     /*! @brief Unsigned integer type. */
     using size_type = typename node_type::size_type;
 
@@ -1188,6 +1190,14 @@ public:
         : node{curr}
     {}
 
+    /**
+     * @brief Constructs an instance from a given base node.
+     * @param curr The base node with which to construct the instance.
+     */
+    meta_type(base_node_type *curr) ENTT_NOEXCEPT
+        : node{curr ? curr->type() : nullptr}
+    {}
+
     /**
      * @brief Returns the type info object of the underlying type.
      * @return The type info object of the underlying type.
@@ -1366,6 +1376,23 @@ public:
         return node->remove_extent();
     }
 
+    /**
+     * @brief Returns a range to use to visit top-level base meta types.
+     * @return An iterable range to use to visit top-level base meta types.
+     */
+    [[nodiscard]] meta_range<meta_type, internal::meta_base_node> base() const ENTT_NOEXCEPT {
+        return node->base;
+    }
+
+    /**
+     * @brief Returns the base meta type associated with a given identifier.
+     * @param id Unique identifier.
+     * @return The base meta type associated with the given identifier, if any.
+     */
+    [[nodiscard]] meta_type base(const id_type id) const {
+        return internal::meta_visit<&node_type::base>([id](const auto *curr) { return curr->type()->id == id; }, node);
+    }
+
     /**
      * @brief Returns a range to use to visit top-level constructors.
      * @return An iterable range to use to visit top-level constructors.

+ 5 - 4
src/entt/meta/range.hpp

@@ -11,9 +11,10 @@ namespace entt {
 
 /**
  * @brief Iterable range to use to iterate all types of meta objects.
- * @tparam Type Type of meta objects iterated.
+ * @tparam Type Type of meta objects returned.
+ * @tparam Node Type of meta nodes iterated.
  */
-template<typename Type>
+template<typename Type, typename Node = typename Type::node_type>
 class meta_range {
     struct range_iterator {
         using difference_type = std::ptrdiff_t;
@@ -21,7 +22,7 @@ class meta_range {
         using pointer = void;
         using reference = value_type;
         using iterator_category = std::input_iterator_tag;
-        using node_type = typename Type::node_type;
+        using node_type = Node;
 
         range_iterator() ENTT_NOEXCEPT = default;
 
@@ -56,7 +57,7 @@ class meta_range {
 
 public:
     /*! @brief Node type. */
-    using node_type = typename Type::node_type;
+    using node_type = Node;
     /*! @brief Input iterator type. */
     using iterator = range_iterator;
 

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

@@ -251,6 +251,22 @@ TEST_F(MetaType, RemoveExtent) {
     ASSERT_EQ(entt::resolve<derived_t>().remove_extent(), entt::resolve<derived_t>());
 }
 
+TEST_F(MetaType, Base) {
+    using namespace entt::literals;
+
+    auto type = entt::resolve<derived_t>();
+    bool iterate = false;
+
+    for(auto curr: type.base()) {
+        ASSERT_EQ(curr, entt::resolve<base_t>());
+        iterate = true;
+    }
+
+    ASSERT_TRUE(iterate);
+    ASSERT_EQ(type.base("base"_hs), entt::resolve<base_t>());
+    ASSERT_FALSE(type.base("esabe"_hs));
+}
+
 TEST_F(MetaType, Ctor) {
     auto type = entt::resolve<clazz_t>();
     int counter{};