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

meta: support to unregister types

Michele Caini 7 жил өмнө
parent
commit
5a3fdd225e

+ 21 - 0
docs/md/meta.md

@@ -11,6 +11,7 @@
 * [Enjoy the runtime](#enjoy-the-runtime)
 * [Named constants and enums](#named-constants-and-enums)
 * [Properties and meta objects](#properties-and-meta-objects)
+* [Unregister types](#unregister-types)
 <!--
 @endcond TURN_OFF_DOXYGEN
 -->
@@ -391,3 +392,23 @@ auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
 Meta properties are objects having a fairly poor interface, all in all. They
 only provide the `key` and the `value` member functions to be used to retrieve
 the key and the value contained in the form of meta any objects, respectively.
+
+# Unregister types
+
+A type registered with the reflection system can also be unregistered. This
+means unregistering all its data members, member functions, conversion functions
+and so on. However, the base classes won't be unregistered, since they don't
+necessarily depend on it. Similarly, implicitly generated types (as an example,
+the meta types implicitly generated for function parameters when needed) won't
+be unregistered.
+
+To unregister a type, users can use the `unregister` function from the global
+namespace:
+
+```cpp
+entt::unregister<my_type>();
+```
+
+This function returns a boolean value that is true if the type is actually
+registered with the reflection system, false otherwise.<br/>
+The type can be re-registered later with a completely different name and form.

+ 157 - 38
src/entt/meta/factory.hpp

@@ -21,6 +21,10 @@ template<typename Type, typename... Property>
 meta_factory<Type> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT;
 
 
+template<typename Type>
+bool unregister() ENTT_NOEXCEPT;
+
+
 /**
  * @brief A meta factory to be used for reflection purposes.
  *
@@ -65,10 +69,10 @@ class meta_factory {
 
     template<typename Owner, typename Property, typename... Other>
     internal::meta_prop_node * properties(Property &&property, Other &&... other) {
-        static auto prop{std::move(property)};
+        static std::decay_t<Property> prop{};
 
         static internal::meta_prop_node node{
-            properties<Owner>(std::forward<Other>(other)...),
+            nullptr,
             []() -> meta_any {
                 return std::get<0>(prop);
             },
@@ -80,6 +84,8 @@ class meta_factory {
             }
         };
 
+        prop = std::forward<Property>(property);
+        node.next = properties<Owner>(std::forward<Other>(other)...);
         ENTT_ASSERT(!duplicate(meta_any{std::get<0>(prop)}, node.next));
         return &node;
     }
@@ -87,9 +93,9 @@ class meta_factory {
     template<typename... Property>
     meta_factory type(hashed_string name, Property &&... property) ENTT_NOEXCEPT {
         static internal::meta_type_node node{
-            name,
-            internal::meta_info<>::type,
-            properties<Type>(std::forward<Property>(property)...),
+            {},
+            nullptr,
+            nullptr,
             std::is_void_v<Type>,
             std::is_integral_v<Type>,
             std::is_floating_point_v<Type>,
@@ -109,6 +115,9 @@ class meta_factory {
             }
         };
 
+        node.name = name;
+        node.next = internal::meta_info<>::type;
+        node.prop = properties<Type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(name, node.next));
         ENTT_ASSERT(!internal::meta_info<Type>::type);
         internal::meta_info<Type>::type = &node;
@@ -117,9 +126,83 @@ class meta_factory {
         return *this;
     }
 
+    void unregister_prop(internal::meta_prop_node **prop) {
+        while(*prop) {
+            auto *node = *prop;
+            *prop = node->next;
+            node->next = nullptr;
+        }
+    }
+
+    void unregister_dtor() {
+        if(auto node = internal::meta_info<Type>::type->dtor; node) {
+            internal::meta_info<Type>::type->dtor = nullptr;
+            *node->underlying = nullptr;
+        }
+    }
+
+    template<auto Member>
+    auto unregister_all(int) -> decltype((internal::meta_info<Type>::type->*Member)->prop, void()) {
+        while(internal::meta_info<Type>::type->*Member) {
+            auto node = internal::meta_info<Type>::type->*Member;
+            internal::meta_info<Type>::type->*Member = node->next;
+            unregister_prop(&node->prop);
+            node->next = nullptr;
+            *node->underlying = nullptr;
+        }
+    }
+
+    template<auto Member>
+    void unregister_all(char) {
+        while(internal::meta_info<Type>::type->*Member) {
+            auto node = internal::meta_info<Type>::type->*Member;
+            internal::meta_info<Type>::type->*Member = node->next;
+            node->next = nullptr;
+            *node->underlying = nullptr;
+        }
+    }
+
+    bool unregister() ENTT_NOEXCEPT {
+        const auto registered = internal::meta_info<Type>::type;
+
+        if(registered) {
+            if(auto *curr = internal::meta_info<>::type; curr == internal::meta_info<Type>::type) {
+                internal::meta_info<>::type = internal::meta_info<Type>::type->next;
+            } else {
+                while(curr && curr->next != internal::meta_info<Type>::type) {
+                    curr = curr->next;
+                }
+
+                if(curr) {
+                    curr->next = internal::meta_info<Type>::type->next;
+                }
+            }
+
+            unregister_prop(&internal::meta_info<Type>::type->prop);
+            unregister_all<&internal::meta_type_node::base>(0);
+            unregister_all<&internal::meta_type_node::conv>(0);
+            unregister_all<&internal::meta_type_node::ctor>(0);
+            unregister_all<&internal::meta_type_node::data>(0);
+            unregister_all<&internal::meta_type_node::func>(0);
+            unregister_dtor();
+
+            internal::meta_info<Type>::type->name = {};
+            internal::meta_info<Type>::type->next = nullptr;
+            internal::meta_info<Type>::type = nullptr;
+        }
+
+        return registered;
+    }
+
     meta_factory() ENTT_NOEXCEPT = default;
 
 public:
+    template<typename Other, typename... Property>
+    friend meta_factory<Other> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT;
+
+    template<typename Other>
+    friend bool unregister() ENTT_NOEXCEPT;
+
     /**
      * @brief Assigns a meta base to a meta type.
      *
@@ -134,8 +217,9 @@ public:
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_base_node node{
-            type->base,
+            &internal::meta_info<Type>::template base<Base>,
             type,
+            nullptr,
             &internal::meta_info<Base>::resolve,
             [](void *instance) -> void * {
                 return static_cast<Base *>(static_cast<Type *>(instance));
@@ -145,6 +229,7 @@ public:
             }
         };
 
+        node.next = type->base;
         ENTT_ASSERT((!internal::meta_info<Type>::template base<Base>));
         internal::meta_info<Type>::template base<Base> = &node;
         type->base = &node;
@@ -167,8 +252,9 @@ public:
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_conv_node node{
-            type->conv,
+            &internal::meta_info<Type>::template conv<To>,
             type,
+            nullptr,
             &internal::meta_info<To>::resolve,
             [](void *instance) -> meta_any {
                 return static_cast<std::decay_t<To>>(*static_cast<Type *>(instance));
@@ -178,6 +264,7 @@ public:
             }
         };
 
+        node.next = type->conv;
         ENTT_ASSERT((!internal::meta_info<Type>::template conv<To>));
         internal::meta_info<Type>::template conv<To> = &node;
         type->conv = &node;
@@ -206,9 +293,10 @@ public:
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_ctor_node node{
-            type->ctor,
+            &internal::meta_info<Type>::template ctor<typename helper_type::args_type>,
             type,
-            properties<typename helper_type::args_type>(std::forward<Property>(property)...),
+            nullptr,
+            nullptr,
             helper_type::size,
             &helper_type::arg,
             [](meta_any * const any) {
@@ -219,6 +307,8 @@ public:
             }
         };
 
+        node.next = type->ctor;
+        node.prop = properties<typename helper_type::args_type>(std::forward<Property>(property)...);
         ENTT_ASSERT((!internal::meta_info<Type>::template ctor<typename helper_type::args_type>));
         internal::meta_info<Type>::template ctor<typename helper_type::args_type> = &node;
         type->ctor = &node;
@@ -244,9 +334,10 @@ public:
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_ctor_node node{
-            type->ctor,
+            &internal::meta_info<Type>::template ctor<typename helper_type::args_type>,
             type,
-            properties<typename helper_type::args_type>(std::forward<Property>(property)...),
+            nullptr,
+            nullptr,
             helper_type::size,
             &helper_type::arg,
             [](meta_any * const any) {
@@ -257,6 +348,8 @@ public:
             }
         };
 
+        node.next = type->ctor;
+        node.prop = properties<typename helper_type::args_type>(std::forward<Property>(property)...);
         ENTT_ASSERT((!internal::meta_info<Type>::template ctor<typename helper_type::args_type>));
         internal::meta_info<Type>::template ctor<typename helper_type::args_type> = &node;
         type->ctor = &node;
@@ -286,6 +379,7 @@ public:
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_dtor_node node{
+            &internal::meta_info<Type>::template dtor<Func>,
             type,
             [](meta_handle handle) {
                 return handle.type() == internal::meta_info<Type>::resolve()->meta()
@@ -322,15 +416,15 @@ public:
     template<auto Data, typename... Property>
     meta_factory data(const char *str, Property &&... property) ENTT_NOEXCEPT {
         auto * const type = internal::meta_info<Type>::resolve();
+        internal::meta_data_node *curr = nullptr;
 
         if constexpr(std::is_same_v<Type, decltype(Data)>) {
-            using owner_type = std::integral_constant<Type, Data>;
-
             static internal::meta_data_node node{
-                hashed_string{str},
-                type->data,
+                &internal::meta_info<Type>::template data<Data>,
+                {},
                 type,
-                properties<owner_type>(std::forward<Property>(property)...),
+                nullptr,
+                nullptr,
                 true,
                 true,
                 &internal::meta_info<Type>::resolve,
@@ -341,18 +435,15 @@ public:
                 }
             };
 
-            ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
-            ENTT_ASSERT((!internal::meta_info<Type>::template data<Data>));
-            internal::meta_info<Type>::template data<Data> = &node;
-            type->data = &node;
+            node.prop = properties<std::integral_constant<Type, Data>>(std::forward<Property>(property)...);
+            curr = &node;
         } else {
-            using owner_type = std::integral_constant<decltype(Data), Data>;
-
             static internal::meta_data_node node{
-                hashed_string{str},
-                type->data,
+                &internal::meta_info<Type>::template data<Data>,
+                {},
                 type,
-                properties<owner_type>(std::forward<Property>(property)...),
+                nullptr,
+                nullptr,
                 std::is_const_v<data_type<Data>>,
                 !std::is_member_object_pointer_v<decltype(Data)>,
                 &internal::meta_info<data_type<Data>>::resolve,
@@ -363,12 +454,17 @@ public:
                 }
             };
 
-            ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
-            ENTT_ASSERT((!internal::meta_info<Type>::template data<Data>));
-            internal::meta_info<Type>::template data<Data> = &node;
-            type->data = &node;
+            node.prop = properties<std::integral_constant<decltype(Data), Data>>(std::forward<Property>(property)...);
+            curr = &node;
         }
 
+        curr->name = hashed_string{str};
+        curr->next = type->data;
+        ENTT_ASSERT(!duplicate(hashed_string{str}, curr->next));
+        ENTT_ASSERT((!internal::meta_info<Type>::template data<Data>));
+        internal::meta_info<Type>::template data<Data> = curr;
+        type->data = curr;
+
         return *this;
     }
 
@@ -401,10 +497,11 @@ public:
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_data_node node{
-            hashed_string{str},
-            type->data,
+            &internal::meta_info<Type>::template data<Setter, Getter>,
+            {},
             type,
-            properties<owner_type>(std::forward<Property>(property)...),
+            nullptr,
+            nullptr,
             false,
             false,
             &internal::meta_info<underlying_type>::resolve,
@@ -415,6 +512,9 @@ public:
             }
         };
 
+        node.name = hashed_string{str};
+        node.next = type->data;
+        node.prop = properties<owner_type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
         ENTT_ASSERT((!internal::meta_info<Type>::template data<Setter, Getter>));
         internal::meta_info<Type>::template data<Setter, Getter> = &node;
@@ -443,10 +543,11 @@ public:
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_func_node node{
-            hashed_string{str},
-            type->func,
+            &internal::meta_info<Type>::template func<Func>,
+            {},
             type,
-            properties<owner_type>(std::forward<Property>(property)...),
+            nullptr,
+            nullptr,
             func_type<Func>::size,
             func_type<Func>::is_const,
             func_type<Func>::is_static,
@@ -460,6 +561,9 @@ public:
             }
         };
 
+        node.name = hashed_string{str};
+        node.next = type->func;
+        node.prop = properties<owner_type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
         ENTT_ASSERT((!internal::meta_info<Type>::template func<Func>));
         internal::meta_info<Type>::template func<Func> = &node;
@@ -467,9 +571,6 @@ public:
 
         return *this;
     }
-
-    template<typename Other, typename... Property>
-    friend meta_factory<Other> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT;
 };
 
 
@@ -510,6 +611,24 @@ inline meta_factory<Type> reflect() ENTT_NOEXCEPT {
 }
 
 
+/**
+ * @brief Basic function to unregister a type.
+ *
+ * This function unregisters a type and all its data members, member functions
+ * and properties, as well as its constructors, destructors and conversion
+ * functions if any.<br/>
+ * Base classes aren't unregistered but the link between the two types is
+ * removed.
+ *
+ * @tparam Type Type to unregister.
+ * @return True if the type to unregister exists, false otherwise.
+ */
+template<typename Type>
+inline bool unregister() ENTT_NOEXCEPT {
+    return meta_factory<Type>().unregister();
+}
+
+
 /**
  * @brief Returns the meta type associated with a given type.
  * @tparam Type Type to use to search for a meta type.

+ 22 - 16
src/entt/meta/meta.hpp

@@ -42,7 +42,7 @@ struct meta_type_node;
 
 
 struct meta_prop_node {
-    meta_prop_node * const next;
+    meta_prop_node * next;
     meta_any(* const key)();
     meta_any(* const value)();
     meta_prop(* const meta)();
@@ -50,8 +50,9 @@ struct meta_prop_node {
 
 
 struct meta_base_node {
-    meta_base_node * const next;
+    meta_base_node ** const underlying;
     meta_type_node * const parent;
+    meta_base_node * next;
     meta_type_node *(* const type)();
     void *(* const cast)(void *);
     meta_base(* const meta)();
@@ -59,8 +60,9 @@ struct meta_base_node {
 
 
 struct meta_conv_node {
-    meta_conv_node * const next;
+    meta_conv_node ** const underlying;
     meta_type_node * const parent;
+    meta_conv_node * next;
     meta_type_node *(* const type)();
     meta_any(* const conv)(void *);
     meta_conv(* const meta)();
@@ -69,9 +71,10 @@ struct meta_conv_node {
 
 struct meta_ctor_node {
     using size_type = std::size_t;
-    meta_ctor_node * const next;
+    meta_ctor_node ** const underlying;
     meta_type_node * const parent;
-    meta_prop_node * const prop;
+    meta_ctor_node * next;
+    meta_prop_node * prop;
     const size_type size;
     meta_type_node *(* const arg)(size_type);
     meta_any(* const invoke)(meta_any * const);
@@ -80,6 +83,7 @@ struct meta_ctor_node {
 
 
 struct meta_dtor_node {
+    meta_dtor_node ** const underlying;
     meta_type_node * const parent;
     bool(* const invoke)(meta_handle);
     meta_dtor(* const meta)();
@@ -87,10 +91,11 @@ struct meta_dtor_node {
 
 
 struct meta_data_node {
-    const hashed_string name;
-    meta_data_node * const next;
+    meta_data_node ** const underlying;
+    hashed_string name;
     meta_type_node * const parent;
-    meta_prop_node * const prop;
+    meta_data_node * next;
+    meta_prop_node * prop;
     const bool is_const;
     const bool is_static;
     meta_type_node *(* const type)();
@@ -102,10 +107,11 @@ struct meta_data_node {
 
 struct meta_func_node {
     using size_type = std::size_t;
-    const hashed_string name;
-    meta_func_node * const next;
+    meta_func_node ** const underlying;
+    hashed_string name;
     meta_type_node * const parent;
-    meta_prop_node * const prop;
+    meta_func_node * next;
+    meta_prop_node * prop;
     const size_type size;
     const bool is_const;
     const bool is_static;
@@ -117,9 +123,9 @@ struct meta_func_node {
 
 
 struct meta_type_node {
-    const hashed_string name;
-    meta_type_node * const next;
-    meta_prop_node * const prop;
+    hashed_string name;
+    meta_type_node * next;
+    meta_prop_node * prop;
     const bool is_void;
     const bool is_integral;
     const bool is_floating_point;
@@ -2016,8 +2022,8 @@ inline bool destroy([[maybe_unused]] meta_handle handle) {
 
 template<typename Type, typename... Args, std::size_t... Indexes>
 inline meta_any construct(meta_any * const args, std::index_sequence<Indexes...>) {
-    std::array<bool, sizeof...(Args)> can_cast{{(args+Indexes)->can_cast<std::decay_t<Args>>()...}};
-    std::array<bool, sizeof...(Args)> can_convert{{(std::get<Indexes>(can_cast) ? false : (args+Indexes)->can_convert<std::decay_t<Args>>())...}};
+    [[maybe_unused]] std::array<bool, sizeof...(Args)> can_cast{{(args+Indexes)->can_cast<std::decay_t<Args>>()...}};
+    [[maybe_unused]] std::array<bool, sizeof...(Args)> can_convert{{(std::get<Indexes>(can_cast) ? false : (args+Indexes)->can_convert<std::decay_t<Args>>())...}};
     meta_any any{};
 
     if(((std::get<Indexes>(can_cast) || std::get<Indexes>(can_convert)) && ...)) {

+ 67 - 2
test/entt/meta/meta.cpp

@@ -183,6 +183,17 @@ struct Meta: public ::testing::Test {
                 .func<&concrete_type::f>("f");
     }
 
+    static void SetUpAfterUnregistration() {
+        entt::reflect<double>().conv<float>();
+
+        entt::reflect<derived_type>("my_type", std::make_pair(properties::prop_bool, false))
+                .ctor<>();
+
+        entt::reflect<another_abstract_type>("your_type")
+                .data<&another_abstract_type::j>("a_data_member")
+                .func<&another_abstract_type::h>("a_member_function");
+    }
+
     void SetUp() override {
         empty_type::counter = 0;
         func_type::value = 0;
@@ -651,7 +662,7 @@ TEST_F(Meta, MetaCtor) {
     ctor.prop([](auto prop) {
         ASSERT_TRUE(prop);
         ASSERT_EQ(prop.key(), properties::prop_bool);
-        ASSERT_EQ(prop.value(), false);
+        ASSERT_FALSE(prop.value().template cast<bool>());
     });
 
     ASSERT_FALSE(ctor.prop(properties::prop_int));
@@ -660,7 +671,7 @@ TEST_F(Meta, MetaCtor) {
 
     ASSERT_TRUE(prop);
     ASSERT_EQ(prop.key(), properties::prop_bool);
-    ASSERT_EQ(prop.value(), false);
+    ASSERT_FALSE(prop.value().template cast<bool>());
 }
 
 TEST_F(Meta, MetaCtorFunc) {
@@ -1492,3 +1503,57 @@ TEST_F(Meta, ArithmeticTypeAndNamedConstants) {
     ASSERT_EQ(type.data("min").get({}).cast<unsigned int>(), 0u);
     ASSERT_EQ(type.data("max").get({}).cast<unsigned int>(), 100u);
 }
+
+TEST_F(Meta, Unregister) {
+    entt::unregister<double>();
+    entt::unregister<char>();
+    entt::unregister<properties>();
+    entt::unregister<unsigned int>();
+    entt::unregister<base_type>();
+    entt::unregister<derived_type>();
+    entt::unregister<empty_type>();
+    entt::unregister<fat_type>();
+    entt::unregister<data_type>();
+    entt::unregister<func_type>();
+    entt::unregister<setter_getter_type>();
+    entt::unregister<an_abstract_type>();
+    entt::unregister<another_abstract_type>();
+    entt::unregister<concrete_type>();
+
+    ASSERT_FALSE(entt::resolve("char"));
+    ASSERT_FALSE(entt::resolve("base"));
+    ASSERT_FALSE(entt::resolve("derived"));
+    ASSERT_FALSE(entt::resolve("empty"));
+    ASSERT_FALSE(entt::resolve("fat"));
+    ASSERT_FALSE(entt::resolve("data"));
+    ASSERT_FALSE(entt::resolve("func"));
+    ASSERT_FALSE(entt::resolve("setter_getter"));
+    ASSERT_FALSE(entt::resolve("an_abstract_type"));
+    ASSERT_FALSE(entt::resolve("another_abstract_type"));
+    ASSERT_FALSE(entt::resolve("concrete"));
+
+    Meta::SetUpAfterUnregistration();
+    entt::meta_any any{42.};
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(any.can_convert<int>());
+    ASSERT_TRUE(any.can_convert<float>());
+
+    ASSERT_FALSE(entt::resolve("derived"));
+    ASSERT_TRUE(entt::resolve("my_type"));
+
+    entt::resolve<derived_type>().prop([](auto prop) {
+        ASSERT_TRUE(prop);
+        ASSERT_EQ(prop.key(), properties::prop_bool);
+        ASSERT_FALSE(prop.value().template cast<bool>());
+    });
+
+    ASSERT_FALSE((entt::resolve<derived_type>().ctor<const base_type &, int, char>()));
+    ASSERT_TRUE((entt::resolve<derived_type>().ctor<>()));
+
+    ASSERT_TRUE(entt::resolve("your_type").data("a_data_member"));
+    ASSERT_FALSE(entt::resolve("your_type").data("another_data_member"));
+
+    ASSERT_TRUE(entt::resolve("your_type").func("a_member_function"));
+    ASSERT_FALSE(entt::resolve("your_type").func("another_member_function"));
+}