Преглед изворни кода

meta, review implicitly generated meta default constructors:
* reduce instantiations and remove their static objects
* these ctors are no longer returned from a direct lookup nor during iterations

Michele Caini пре 4 година
родитељ
комит
135d6915ee
6 измењених фајлова са 82 додато и 105 уклоњено
  1. 12 5
      docs/md/meta.md
  2. 0 1
      src/entt/meta/factory.hpp
  3. 46 45
      src/entt/meta/meta.hpp
  4. 5 14
      src/entt/meta/node.hpp
  5. 12 30
      test/entt/meta/meta_ctor.cpp
  6. 7 10
      test/entt/meta/meta_type.cpp

+ 12 - 5
docs/md/meta.md

@@ -742,11 +742,18 @@ entt::resolve<int>().construct();
 Where the meta type can be for example the one returned from a meta container,
 useful for building keys without knowing or having to register the actual types.
 
-In all cases, when users register custom defaul constructors, they are preferred
-both during searches and when the `construct` member function is invoked.<br/>
-However, the implicitly generated default constructor will always be returned,
-either if one is not explicitly specified or if all constructors are iterated
-for some reason (in this case, it will always be the last element).
+In all cases, when users register default constructors, they are preferred both
+during searches and when the `construct` member function is invoked.<br/>
+Moreover, implicitly generated default constructors are never returned when
+iterating registered constructors nor when looking up constructors from meta
+types:
+
+```cpp
+entt::meta_ctor ctor = entt::resolve<int>().ctor<>();
+```
+
+In other terms, `ctor` is an invalid meta object unless users explicitly
+registered a meta constructor that takes no arguments for the `int` type.
 
 ## Policies: the more, the less
 

+ 0 - 1
src/entt/meta/factory.hpp

@@ -572,7 +572,6 @@ inline void meta_reset(const id_type id) ENTT_NOEXCEPT {
             clear_chain(&node->func, &internal::meta_func_node::prop);
 
             node->id = {};
-            node->ctor = node->def_ctor;
             node->dtor = nullptr;
             *it = std::exchange(node->next, nullptr);
 

+ 46 - 45
src/entt/meta/meta.hpp

@@ -333,8 +333,8 @@ public:
     /**
      * @brief Sets the value of a given variable.
      *
-     * The type of the value must be such that a cast or conversion to the type
-     * of the variable is possible. Otherwise, invoking the setter does nothing.
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
      *
      * @tparam Type Type of value to assign.
      * @param id Unique identifier.
@@ -737,8 +737,8 @@ struct meta_ctor {
     /**
      * @brief Creates an instance of the underlying type, if possible.
      *
-     * Parameters must be such that a cast or conversion to the required types
-     * is possible. Otherwise, an empty and thus invalid wrapper is returned.
+     * Parameters are such that a cast or conversion to the required types is
+     * possible. Otherwise, an empty and thus invalid wrapper is returned.
      *
      * @param args Parameters to use to construct the instance.
      * @param sz Number of parameters to use to construct the instance.
@@ -764,17 +764,17 @@ struct meta_ctor {
     }
 
     /**
-     * @brief Returns a range to use to visit all properties.
-     * @return An iterable range to use to visit all properties.
+     * @brief Returns a range to visit registered meta properties.
+     * @return An iterable range to visit registered meta properties.
      */
     [[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
         return node->prop;
     }
 
     /**
-     * @brief Returns the property associated with a given key.
+     * @brief Lookup function for registered meta properties.
      * @param key The key to use to search for a property.
-     * @return The property associated with the given key, if any.
+     * @return The registered meta property for the given key, if any.
      */
     [[nodiscard]] meta_prop prop(meta_any key) const {
         for(auto curr: prop()) {
@@ -839,8 +839,8 @@ struct meta_data {
      * It must be possible to cast the instance to the parent type of the data
      * member. Otherwise, invoking the setter results in an undefined
      * behavior.<br/>
-     * The type of the value must be such that a cast or conversion to the type
-     * of the variable is possible. Otherwise, invoking the setter does nothing.
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
      *
      * @tparam Type Type of value to assign.
      * @param instance An opaque instance of the underlying type.
@@ -871,9 +871,9 @@ struct meta_data {
     }
 
     /**
-     * @brief Returns the property associated with a given key.
+     * @brief Lookup function for registered meta properties.
      * @param key The key to use to search for a property.
-     * @return The property associated with the given key, if any.
+     * @return The registered meta property for the given key, if any.
      */
     [[nodiscard]] meta_prop prop(meta_any key) const {
         for(auto curr: prop()) {
@@ -993,9 +993,9 @@ struct meta_func {
     }
 
     /**
-     * @brief Returns the property associated with a given key.
+     * @brief Lookup function for registered meta properties.
      * @param key The key to use to search for a property.
-     * @return The property associated with the given key, if any.
+     * @return The registered meta property for the given key, if any.
      */
     [[nodiscard]] meta_prop prop(meta_any key) const {
         for(auto curr: prop()) {
@@ -1194,8 +1194,8 @@ public:
     }
 
     /**
-     * @brief Returns the number of template arguments, if any.
-     * @return The number of template arguments, if any.
+     * @brief Returns the number of template arguments.
+     * @return The number of template arguments.
      */
     [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT {
         return node->templ ? node->templ->arity : size_type{};
@@ -1222,34 +1222,34 @@ public:
     }
 
     /**
-     * @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.
+     * @brief Returns a range to visit registered top-level base meta types.
+     * @return An iterable range to visit registered 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.
+     * @brief Lookup function for registered base meta types.
      * @param id Unique identifier.
-     * @return The base meta type associated with the given identifier, if any.
+     * @return The registered base meta type for the given identifier, if any.
      */
     [[nodiscard]] meta_type base(const id_type id) const {
         return internal::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.
+     * @brief Returns a range to visit registered top-level constructors.
+     * @return An iterable range to visit registered top-level constructors.
      */
     [[nodiscard]] meta_range<meta_ctor> ctor() const ENTT_NOEXCEPT {
         return node->ctor;
     }
 
     /**
-     * @brief Returns a constructor for a given list of types of arguments.
+     * @brief Lookup function for registered meta constructors.
      * @tparam Args Constructor arguments.
-     * @return The requested constructor, if any.
+     * @return The registered meta constructor for the given arguments, if any.
      */
     template<typename... Args>
     [[nodiscard]] meta_ctor ctor() const {
@@ -1257,42 +1257,42 @@ public:
     }
 
     /**
-     * @brief Returns a range to use to visit top-level data.
-     * @return An iterable range to use to visit top-level data.
+     * @brief Returns a range to visit registered top-level meta data.
+     * @return An iterable range to visit registered top-level meta data.
      */
     [[nodiscard]] meta_range<meta_data> data() const ENTT_NOEXCEPT {
         return node->data;
     }
 
     /**
-     * @brief Returns the data associated with a given identifier.
+     * @brief Lookup function for registered meta data.
      *
-     * The data of the base classes will also be visited, if any.
+     * Registered meta data of base classes will also be visited.
      *
      * @param id Unique identifier.
-     * @return The data associated with the given identifier, if any.
+     * @return The registered meta data for the given identifier, if any.
      */
     [[nodiscard]] meta_data data(const id_type id) const {
         return internal::visit<&node_type::data>([id](const auto *curr) { return curr->id == id; }, node);
     }
 
     /**
-     * @brief Returns a range to use to visit top-level functions.
-     * @return An iterable range to use to visit top-level functions.
+     * @brief Returns a range to visit registered top-level functions.
+     * @return An iterable range to visit registered top-level functions.
      */
     [[nodiscard]] meta_range<meta_func> func() const ENTT_NOEXCEPT {
         return node->func;
     }
 
     /**
-     * @brief Returns the function associated with a given identifier.
+     * @brief Lookup function for registered meta functions.
      *
-     * The functions of the base classes will also be visited, if any.<br/>
-     * In the case of overloaded functions, the first one with the required
+     * Registered meta functions of base classes will also be visited.<br/>
+     * In case of overloaded functions, the first one with the required
      * identifier will be returned.
      *
      * @param id Unique identifier.
-     * @return The function associated with the given identifier, if any.
+     * @return The registered meta function for the given identifier, if any.
      */
     [[nodiscard]] meta_func func(const id_type id) const {
         return internal::visit<&node_type::func>([id](const auto *curr) { return curr->id == id; }, node);
@@ -1301,8 +1301,9 @@ public:
     /**
      * @brief Creates an instance of the underlying type, if possible.
      *
-     * Parameters must be such that a cast or conversion to the required types
-     * is possible. Otherwise, an empty and thus invalid wrapper is returned.
+     * Parameters are such that a cast or conversion to the required types is
+     * possible. Otherwise, an empty and thus invalid wrapper is returned.<br/>
+     * If suitable, the implicitly generated default constructor is used.
      *
      * @param args Parameters to use to construct the instance.
      * @param sz Number of parameters to use to construct the instance.
@@ -1317,7 +1318,7 @@ public:
             }
         }
 
-        return {};
+        return (!sz && node->factory) ? node->factory() : meta_any{};
     }
 
     /**
@@ -1402,8 +1403,8 @@ public:
      * It must be possible to cast the instance to the parent type of the data
      * member. Otherwise, invoking the setter results in an undefined
      * behavior.<br/>
-     * The type of the value must be such that a cast or conversion to the type
-     * of the variable is possible. Otherwise, invoking the setter does nothing.
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
      *
      * @tparam Type Type of value to assign.
      * @param id Unique identifier.
@@ -1433,20 +1434,20 @@ public:
     }
 
     /**
-     * @brief Returns a range to use to visit top-level properties.
-     * @return An iterable range to use to visit top-level properties.
+     * @brief Returns a range to visit registered top-level meta properties.
+     * @return An iterable range to visit registered top-level meta properties.
      */
     [[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
         return node->prop;
     }
 
     /**
-     * @brief Returns the property associated with a given key.
+     * @brief Lookup function for meta properties.
      *
-     * Properties of the base classes will also be visited, if any.
+     * Properties of base classes are also visited.
      *
      * @param key The key to use to search for a property.
-     * @return The property associated with the given key, if any.
+     * @return The registered meta property for the given key, if any.
      */
     [[nodiscard]] meta_prop prop(meta_any key) const {
         return internal::visit<&internal::meta_type_node::prop>([&key](const auto *curr) { return curr->id == key; }, node);

+ 5 - 14
src/entt/meta/node.hpp

@@ -123,8 +123,8 @@ struct meta_type_node {
     meta_prop_node * prop;
     const size_type size_of;
     meta_traits traits;
+    meta_any(* const factory)();
     const meta_template_node *const templ;
-    meta_ctor_node * const def_ctor;
     meta_ctor_node *ctor{nullptr};
     meta_base_node *base{nullptr};
     meta_conv_node *conv{nullptr};
@@ -142,17 +142,9 @@ template<typename Type>
 class ENTT_API meta_node {
     static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
 
-    [[nodiscard]] static meta_ctor_node * meta_default_constructor([[maybe_unused]] meta_type_node *type) ENTT_NOEXCEPT {
+    [[nodiscard]] static decltype(meta_type_node::factory) meta_default_constructor() ENTT_NOEXCEPT {
         if constexpr(std::is_default_constructible_v<Type>) {
-            static meta_ctor_node node{
-                nullptr,
-                nullptr,
-                0u,
-                nullptr,
-                [](meta_any * const) { return meta_any{std::in_place_type<Type>}; }
-            };
-
-            return &node;
+            return +[]() { return meta_any{std::in_place_type<Type>}; };
         } else {
             return nullptr;
         }
@@ -193,9 +185,8 @@ public:
                 | (is_meta_pointer_like_v<Type> ? internal::meta_traits::IS_META_POINTER_LIKE : internal::meta_traits::IS_NONE)
                 | (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::IS_META_SEQUENCE_CONTAINER : internal::meta_traits::IS_NONE)
                 | (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::IS_META_ASSOCIATIVE_CONTAINER : internal::meta_traits::IS_NONE),
-            meta_template_info(),
-            meta_default_constructor(&node),
-            meta_default_constructor(&node)
+            meta_default_constructor(),
+            meta_template_info()
         };
 
         return &node;

+ 12 - 30
test/entt/meta/meta_ctor.cpp

@@ -220,19 +220,12 @@ TEST_F(MetaCtor, ExternalMemberFunction) {
 
 TEST_F(MetaCtor, ImplicitlyGeneratedDefaultConstructor) {
     auto type = entt::resolve<int>();
-    int counter{};
 
-    for([[maybe_unused]] auto curr: type.ctor()) {
-        ++counter;
-    }
-
-    // default constructor is implicitly generated
-    ASSERT_EQ(counter, 1);
-    ASSERT_TRUE(type.ctor<>());
-    ASSERT_EQ(type.ctor<>().arity(), 0u);
-    ASSERT_EQ(type.ctor<>().arg(0), entt::meta_type{});
+    // implicitly generated default constructor is not listed among registered
+    ASSERT_EQ(type.ctor().begin(), type.ctor().end());
+    ASSERT_FALSE(type.ctor<>());
 
-    auto any = type.ctor<>().invoke();
+    auto any = type.construct();
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<int>());
@@ -241,15 +234,12 @@ TEST_F(MetaCtor, ImplicitlyGeneratedDefaultConstructor) {
 
 TEST_F(MetaCtor, OverrideImplicitlyGeneratedDefaultConstructor) {
     auto type = entt::resolve<double>();
-    int counter{};
 
-    for([[maybe_unused]] auto curr: type.ctor()) {
-        ++counter;
-    }
-
-    // default constructor is implicitly generated
-    ASSERT_EQ(counter, 2);
+    // implicitly generated default constructor is not listed among registered
+    ASSERT_EQ(++type.ctor().begin(), type.ctor().end());
     ASSERT_TRUE(type.ctor<>());
+    ASSERT_EQ(type.ctor<>().arity(), 0u);
+    ASSERT_EQ(type.ctor<>().arg(0), entt::meta_type{});
 
     auto any = type.ctor<>().invoke();
 
@@ -260,15 +250,8 @@ TEST_F(MetaCtor, OverrideImplicitlyGeneratedDefaultConstructor) {
 
 TEST_F(MetaCtor, NonDefaultConstructibleType) {
     auto type = entt::resolve<clazz_t>();
-    int counter{};
-
-    for([[maybe_unused]] auto curr: type.ctor()) {
-        ++counter;
-    }
-
-    // the implicitly generated default constructor doesn't exist
-    ASSERT_EQ(counter, 5);
-    ASSERT_FALSE(type.ctor<>());
+    // no implicitly generated default constructor
+    ASSERT_FALSE(type.construct());
 }
 
 TEST_F(MetaCtor, ReRegistration) {
@@ -277,7 +260,6 @@ TEST_F(MetaCtor, ReRegistration) {
     auto *node = entt::internal::meta_info<double>::resolve();
 
     ASSERT_NE(node->ctor, nullptr);
-    // default constructor is implicitly generated
-    ASSERT_NE(node->ctor->next, nullptr);
-    ASSERT_EQ(node->ctor->next->next, nullptr);
+    // implicitly generated default constructor is not cleared
+    ASSERT_NE(node->factory, nullptr);
 }

+ 7 - 10
test/entt/meta/meta_type.cpp

@@ -284,9 +284,8 @@ TEST_F(MetaType, Ctor) {
         ++counter;
     }
 
-    // we only register a constructor, the default one is implicitly generated for us
-    ASSERT_EQ(counter, 2);
-    ASSERT_TRUE((type.ctor<>()));
+    ASSERT_EQ(counter, 1);
+    ASSERT_FALSE((type.ctor<>()));
     ASSERT_TRUE((type.ctor<const base_t &, int>()));
     ASSERT_TRUE((type.ctor<const derived_t &, double>()));
 
@@ -452,7 +451,7 @@ TEST_F(MetaType, Reset) {
     ASSERT_TRUE(entt::resolve<clazz_t>().data("value"_hs));
     ASSERT_TRUE((entt::resolve<clazz_t>().ctor<const base_t &, int>()));
     // implicitly generated default constructor
-    ASSERT_TRUE(entt::resolve<clazz_t>().ctor<>());
+    ASSERT_TRUE(entt::resolve<clazz_t>().construct());
 
     entt::meta_reset("clazz"_hs);
 
@@ -461,14 +460,12 @@ TEST_F(MetaType, Reset) {
     ASSERT_FALSE(entt::resolve<clazz_t>().prop(property_t::value));
     ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
     ASSERT_FALSE((entt::resolve<clazz_t>().ctor<const base_t &, int>()));
-    // the implicitly generated default constructor is there after a reset
-    ASSERT_TRUE(entt::resolve<clazz_t>().ctor<>());
+    // implicitly generated default constructor is not cleared
+    ASSERT_TRUE(entt::resolve<clazz_t>().construct());
 
     entt::meta<clazz_t>().type("clazz"_hs);
 
     ASSERT_TRUE(entt::resolve("clazz"_hs));
-    // the implicitly generated default constructor must be there in any case
-    ASSERT_TRUE(entt::resolve<clazz_t>().ctor<>());
 }
 
 TEST_F(MetaType, ResetAll) {
@@ -604,8 +601,8 @@ TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
     ASSERT_EQ(*entt::internal::meta_context::global(), nullptr);
 
     ASSERT_FALSE(entt::resolve<clazz_t>().prop(property_t::value));
-    // the implicitly generated default constructor is there after a reset
-    ASSERT_TRUE(entt::resolve<clazz_t>().ctor<>());
+    // implicitly generated default constructor is not cleared
+    ASSERT_TRUE(entt::resolve<clazz_t>().construct());
     ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
     ASSERT_FALSE(entt::resolve<clazz_t>().func("member"_hs));