Jelajahi Sumber

meta: introduce extended meta factory

Michele Caini 6 tahun lalu
induk
melakukan
ab12f29ebf
4 mengubah file dengan 153 tambahan dan 105 penghapusan
  1. 4 2
      TODO
  2. 14 0
      src/entt/core/type_traits.hpp
  3. 88 87
      src/entt/meta/factory.hpp
  4. 47 16
      test/entt/meta/meta.cpp

+ 4 - 2
TODO

@@ -34,8 +34,10 @@
 * use [[nodiscard]] consistently for safety purposes
 
 * make meta work across boundaries
-  - type(id, props...) -> type(id).properties(props...) with internal meta_prop_node ** as a sink
+  - ::prop: use meta_any as arguments rather than sfinae all around
+  - extend and make factory::prop more flexible
   - entt::reflect<T>().type("foo"_hs, entt::as_property); or similar
-  - name-less reflect with properties
   - forbid reflecting two times the same type
+  - name-less reflect/type (for named types)
+  - a better context
   - tests, doc

+ 14 - 0
src/entt/core/type_traits.hpp

@@ -120,6 +120,20 @@ template<typename Type>
 using type_list_unique_t = typename type_list_unique<Type>::type;
 
 
+/**
+ * @brief A class to use to push around constexpr properties, nothing more.
+ * @tparam Key Property key.
+ * @tparam Value Property value.
+ */
+template<auto Key, auto Value>
+struct property {
+    /*! @brief Property key. */
+    static constexpr auto key = Key;
+    /*! @brief Property value. */
+    static constexpr auto value = Value;
+};
+
+
 /**
  * @brief Provides the member constant `value` to true if a given type is
  * equality comparable, false otherwise.

+ 88 - 87
src/entt/meta/factory.hpp

@@ -9,6 +9,7 @@
 #include <functional>
 #include <type_traits>
 #include "../config/config.h"
+#include "../core/type_traits.hpp"
 #include "policy.hpp"
 #include "meta.hpp"
 
@@ -235,6 +236,10 @@ meta_any invoke([[maybe_unused]] meta_handle handle, meta_any *args, std::index_
  */
 
 
+template<typename, typename...>
+class extended_meta_factory;
+
+
 /**
  * @brief A meta factory to be used for reflection purposes.
  *
@@ -257,58 +262,22 @@ class meta_factory {
         return node && (node->identifier == identifier || duplicate(identifier, node->next));
     }
 
-    bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
-        return node && (node->key() == key || duplicate(key, node->next));
-    }
-
-    template<typename>
-    internal::meta_prop_node * properties() {
-        return nullptr;
-    }
-
-    template<typename Owner, typename Property, typename... Other>
-    internal::meta_prop_node * properties(Property &&property, Other &&... other) {
-        static std::remove_cv_t<std::remove_reference_t<Property>> prop{};
-
-        static internal::meta_prop_node node{
-            nullptr,
-            []() -> meta_any {
-                return std::as_const(std::get<0>(prop));
-            },
-            []() -> meta_any {
-                return std::as_const(std::get<1>(prop));
-            }
-        };
-
-        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;
-    }
-
 public:
-    /*! @brief Default constructor. */
-    meta_factory() ENTT_NOEXCEPT = default;
-
     /**
-     * @brief Extends a meta type by assigning it an identifier and properties.
-     * @tparam Property Types of properties to assign to the meta type.
+     * @brief Extends a meta type by assigning it an identifier.
      * @param identifier Unique identifier.
-     * @param property Properties to assign to the meta type.
-     * @return A meta factory for the parent type.
+     * @return An extended meta factory for the parent type.
      */
-    template<typename... Property>
-    meta_factory type(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
+    auto type(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
         auto * const node = internal::meta_info<Type>::resolve();
 
         node->identifier = identifier;
-        node->prop = properties<Type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(identifier, *internal::meta_info<>::ctx));
         ENTT_ASSERT(!duplicate(node, *internal::meta_info<>::ctx));
         node->next = *internal::meta_info<>::ctx;
         *internal::meta_info<>::ctx = node;
 
-        return *this;
+        return extended_meta_factory<Type>{&node->prop};
     }
 
     /**
@@ -320,7 +289,7 @@ public:
      * @return A meta factory for the parent type.
      */
     template<typename Base>
-    meta_factory base() ENTT_NOEXCEPT {
+    auto base() ENTT_NOEXCEPT {
         static_assert(std::is_base_of_v<Base, Type>);
         auto * const type = internal::meta_info<Type>::resolve();
 
@@ -337,7 +306,7 @@ public:
         node.next = type->base;
         type->base = &node;
 
-        return *this;
+        return meta_factory<Type>{};
     }
 
     /**
@@ -350,7 +319,7 @@ public:
      * @return A meta factory for the parent type.
      */
     template<typename To>
-    meta_factory conv() ENTT_NOEXCEPT {
+    auto conv() ENTT_NOEXCEPT {
         static_assert(std::is_convertible_v<Type, To>);
         auto * const type = internal::meta_info<Type>::resolve();
 
@@ -367,7 +336,7 @@ public:
         node.next = type->conv;
         type->conv = &node;
 
-        return *this;
+        return meta_factory<Type>{};
     }
 
     /**
@@ -383,7 +352,7 @@ public:
      * @return A meta factory for the parent type.
      */
     template<auto Candidate>
-    meta_factory conv() ENTT_NOEXCEPT {
+    auto conv() ENTT_NOEXCEPT {
         using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
         auto * const type = internal::meta_info<Type>::resolve();
 
@@ -400,7 +369,7 @@ public:
         node.next = type->conv;
         type->conv = &node;
 
-        return *this;
+        return meta_factory<Type>{};
     }
 
     /**
@@ -414,12 +383,10 @@ public:
      *
      * @tparam Func The actual function to use as a constructor.
      * @tparam Policy Optional policy (no policy set by default).
-     * @tparam Property Types of properties to assign to the meta data.
-     * @param property Properties to assign to the meta data.
-     * @return A meta factory for the parent type.
+     * @return An extended meta factory for the parent type.
      */
-    template<auto Func, typename Policy = as_is_t, typename... Property>
-    meta_factory ctor(Property &&... property) ENTT_NOEXCEPT {
+    template<auto Func, typename Policy = as_is_t>
+    auto ctor() ENTT_NOEXCEPT {
         using helper_type = internal::meta_function_helper_t<decltype(Func)>;
         static_assert(std::is_same_v<typename helper_type::return_type, Type>);
         auto * const type = internal::meta_info<Type>::resolve();
@@ -435,12 +402,11 @@ public:
             }
         };
 
-        node.prop = properties<typename helper_type::args_type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(&node, type->ctor));
         node.next = type->ctor;
         type->ctor = &node;
 
-        return *this;
+        return extended_meta_factory<Type, std::integral_constant<decltype(Func), Func>>{&node.prop};
     }
 
     /**
@@ -451,12 +417,10 @@ public:
      * type that can be invoked with parameters whose types are those given.
      *
      * @tparam Args Types of arguments to use to construct an instance.
-     * @tparam Property Types of properties to assign to the meta data.
-     * @param property Properties to assign to the meta data.
-     * @return A meta factory for the parent type.
+     * @return An extended meta factory for the parent type.
      */
-    template<typename... Args, typename... Property>
-    meta_factory ctor(Property &&... property) ENTT_NOEXCEPT {
+    template<typename... Args>
+    auto ctor() ENTT_NOEXCEPT {
         using helper_type = internal::meta_function_helper_t<Type(*)(Args...)>;
         auto * const type = internal::meta_info<Type>::resolve();
 
@@ -471,12 +435,11 @@ public:
             }
         };
 
-        node.prop = properties<typename helper_type::args_type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(&node, type->ctor));
         node.next = type->ctor;
         type->ctor = &node;
 
-        return *this;
+        return extended_meta_factory<Type, Type(Args...)>{&node.prop};
     }
 
     /**
@@ -496,7 +459,7 @@ public:
      * @return A meta factory for the parent type.
      */
     template<auto Func>
-    meta_factory dtor() ENTT_NOEXCEPT {
+    auto dtor() ENTT_NOEXCEPT {
         static_assert(std::is_invocable_v<decltype(Func), Type &>);
         auto * const type = internal::meta_info<Type>::resolve();
 
@@ -516,7 +479,7 @@ public:
         ENTT_ASSERT(!type->dtor);
         type->dtor = &node;
 
-        return *this;
+        return meta_factory<Type>{};
     }
 
     /**
@@ -529,13 +492,11 @@ public:
      *
      * @tparam Data The actual variable to attach to the meta type.
      * @tparam Policy Optional policy (no policy set by default).
-     * @tparam Property Types of properties to assign to the meta data.
      * @param identifier Unique identifier.
-     * @param property Properties to assign to the meta data.
-     * @return A meta factory for the parent type.
+     * @return An extended meta factory for the parent type.
      */
-    template<auto Data, typename Policy = as_is_t, typename... Property>
-    meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
+    template<auto Data, typename Policy = as_is_t>
+    auto data(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
         auto * const type = internal::meta_info<Type>::resolve();
         internal::meta_data_node *curr = nullptr;
 
@@ -554,7 +515,6 @@ public:
                 [](meta_handle, meta_any) -> meta_any { return Data; }
             };
 
-            node.prop = properties<std::integral_constant<Type, Data>>(std::forward<Property>(property)...);
             curr = &node;
         } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
             using data_type = std::remove_reference_t<decltype(std::declval<Type>().*Data)>;
@@ -571,7 +531,6 @@ public:
                 &internal::getter<Type, Data, Policy>
             };
 
-            node.prop = properties<std::integral_constant<decltype(Data), Data>>(std::forward<Property>(property)...);
             curr = &node;
         } else {
             static_assert(std::is_pointer_v<std::decay_t<decltype(Data)>>);
@@ -589,7 +548,6 @@ public:
                 &internal::getter<Type, Data, Policy>
             };
 
-            node.prop = properties<std::integral_constant<decltype(Data), Data>>(std::forward<Property>(property)...);
             curr = &node;
         }
 
@@ -599,7 +557,7 @@ public:
         curr->next = type->data;
         type->data = curr;
 
-        return *this;
+        return extended_meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&curr->prop};
     }
 
     /**
@@ -619,14 +577,11 @@ public:
      * @tparam Setter The actual function to use as a setter.
      * @tparam Getter The actual function to use as a getter.
      * @tparam Policy Optional policy (no policy set by default).
-     * @tparam Property Types of properties to assign to the meta data.
      * @param identifier Unique identifier.
-     * @param property Properties to assign to the meta data.
-     * @return A meta factory for the parent type.
+     * @return An extended meta factory for the parent type.
      */
-    template<auto Setter, auto Getter, typename Policy = as_is_t, typename... Property>
-    meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
-        using owner_type = std::tuple<std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>;
+    template<auto Setter, auto Getter, typename Policy = as_is_t>
+    auto data(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
         using underlying_type = std::invoke_result_t<decltype(Getter), Type &>;
         static_assert(std::is_invocable_v<decltype(Setter), Type &, underlying_type>);
         auto * const type = internal::meta_info<Type>::resolve();
@@ -644,13 +599,12 @@ public:
         };
 
         node.identifier = identifier;
-        node.prop = properties<owner_type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(identifier, type->data));
         ENTT_ASSERT(!duplicate(&node, type->data));
         node.next = type->data;
         type->data = &node;
 
-        return *this;
+        return extended_meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
     }
 
     /**
@@ -663,14 +617,11 @@ public:
      *
      * @tparam Candidate The actual function to attach to the meta type.
      * @tparam Policy Optional policy (no policy set by default).
-     * @tparam Property Types of properties to assign to the meta function.
      * @param identifier Unique identifier.
-     * @param property Properties to assign to the meta function.
-     * @return A meta factory for the parent type.
+     * @return An extended meta factory for the parent type.
      */
-    template<auto Candidate, typename Policy = as_is_t, typename... Property>
-    meta_factory func(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
-        using owner_type = std::integral_constant<decltype(Candidate), Candidate>;
+    template<auto Candidate, typename Policy = as_is_t>
+    auto func(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
         using helper_type = internal::meta_function_helper_t<decltype(Candidate)>;
         auto * const type = internal::meta_info<Type>::resolve();
 
@@ -690,13 +641,12 @@ public:
         };
 
         node.identifier = identifier;
-        node.prop = properties<owner_type>(std::forward<Property>(property)...);
         ENTT_ASSERT(!duplicate(identifier, type->func));
         ENTT_ASSERT(!duplicate(&node, type->func));
         node.next = type->func;
         type->func = &node;
 
-        return *this;
+        return extended_meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
     }
 
     /**
@@ -713,6 +663,57 @@ public:
 };
 
 
+/**
+ * @brief Extended meta factory to be used for reflection purposes.
+ * @tparam Type Reflected type for which the factory was created.
+ */
+template<typename Type, typename...>
+class extended_meta_factory: public meta_factory<Type> {
+    /*! @brief A meta factory is allowed to _extend_ itself. */
+    friend class meta_factory<Type>;
+
+    bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
+        return node && (node->key() == key || duplicate(key, node->next));
+    }
+
+    template<auto Key, auto Value>
+    void prop(property<Key, Value>) {
+        static internal::meta_prop_node node{
+            *props,
+            []() -> meta_any {
+                return Key;
+            },
+            []() -> meta_any {
+                return Value;
+            }
+        };
+
+        ENTT_ASSERT(!duplicate(meta_any{Key}, *props));
+        *props = &node;
+    }
+
+    extended_meta_factory(entt::internal::meta_prop_node **target)
+        : props{target}
+    {}
+
+public:
+    /**
+     * @brief Assigns properties to the last meta object created.
+     * @tparam Property Properties to assign to the meta object.
+     * @return A meta factory for the parent type.
+     */
+    template<typename... Property>
+    auto prop() {
+        ENTT_ASSERT(props);
+        (prop(Property{}), ...);
+        return *this;
+    }
+
+private:
+    entt::internal::meta_prop_node **props{nullptr};
+};
+
+
 /**
  * @brief Utility function to use for reflection.
  *

+ 47 - 16
test/entt/meta/meta.cpp

@@ -146,12 +146,16 @@ struct Meta: ::testing::Test {
         entt::meta<double>().conv<int>();
 
         entt::meta<char>()
-                .type("char"_hs, std::make_pair(properties::prop_int, 42))
+                .type("char"_hs)
+                    .prop<entt::property<properties::prop_int, 42>>()
                 .data<&set<char>, &get<char>>("value"_hs);
 
         entt::meta<properties>()
                 .data<properties::prop_bool>("prop_bool"_hs)
+                    .prop<entt::property<properties::prop_int, 0>>()
                 .data<properties::prop_int>("prop_int"_hs)
+                    .prop<entt::property<properties::prop_bool, true>>()
+                    .prop<entt::property<properties::prop_int, 0>>()
                 .data<&set<properties>, &get<properties>>("value"_hs);
 
         entt::meta<unsigned int>().data<0u>("min"_hs).data<100u>("max"_hs);
@@ -160,10 +164,13 @@ struct Meta: ::testing::Test {
                 .type("base"_hs);
 
         entt::meta<derived_type>()
-                .type("derived"_hs, std::make_pair(properties::prop_int, 99))
+                .type("derived"_hs)
+                    .prop<entt::property<properties::prop_int, 99>>()
                 .base<base_type>()
-                .ctor<const base_type &, int, char>(std::make_pair(properties::prop_bool, false))
-                .ctor<&derived_factory>(std::make_pair(properties::prop_int, 42))
+                .ctor<const base_type &, int, char>()
+                    .prop<entt::property<properties::prop_bool, false>>()
+                .ctor<&derived_factory>()
+                    .prop<entt::property<properties::prop_int, 42>>()
                 .conv<&derived_type::f>()
                 .conv<&derived_type::g>();
 
@@ -178,10 +185,14 @@ struct Meta: ::testing::Test {
 
         entt::meta<data_type>()
                 .type("data"_hs)
-                .data<&data_type::i, entt::as_alias_t>("i"_hs, std::make_pair(properties::prop_int, 0))
-                .data<&data_type::j>("j"_hs, std::make_pair(properties::prop_int, 1))
-                .data<&data_type::h>("h"_hs, std::make_pair(properties::prop_int, 2))
-                .data<&data_type::k>("k"_hs, std::make_pair(properties::prop_int, 3))
+                .data<&data_type::i, entt::as_alias_t>("i"_hs)
+                    .prop<entt::property<properties::prop_int, 0>>()
+                .data<&data_type::j>("j"_hs)
+                    .prop<entt::property<properties::prop_int, 1>>()
+                .data<&data_type::h>("h"_hs)
+                    .prop<entt::property<properties::prop_int, 2>>()
+                .data<&data_type::k>("k"_hs)
+                    .prop<entt::property<properties::prop_int, 3>>()
                 .data<&data_type::empty>("empty"_hs)
                 .data<&data_type::v, entt::as_void_t>("v"_hs);
 
@@ -193,11 +204,16 @@ struct Meta: ::testing::Test {
         entt::meta<func_type>()
                 .type("func"_hs)
                 .func<entt::overload<int(const base_type &, int, int)>(&func_type::f)>("f3"_hs)
-                .func<entt::overload<int(int, int)>(&func_type::f)>("f2"_hs, std::make_pair(properties::prop_bool, false))
-                .func<entt::overload<int(int) const>(&func_type::f)>("f1"_hs, std::make_pair(properties::prop_bool, false))
-                .func<&func_type::g>("g"_hs, std::make_pair(properties::prop_bool, false))
-                .func<&func_type::h>("h"_hs, std::make_pair(properties::prop_bool, false))
-                .func<&func_type::k>("k"_hs, std::make_pair(properties::prop_bool, false))
+                .func<entt::overload<int(int, int)>(&func_type::f)>("f2"_hs)
+                    .prop<entt::property<properties::prop_bool, false>>()
+                .func<entt::overload<int(int) const>(&func_type::f)>("f1"_hs)
+                    .prop<entt::property<properties::prop_bool, false>>()
+                .func<&func_type::g>("g"_hs)
+                    .prop<entt::property<properties::prop_bool, false>>()
+                .func<&func_type::h>("h"_hs)
+                    .prop<entt::property<properties::prop_bool, false>>()
+                .func<&func_type::k>("k"_hs)
+                    .prop<entt::property<properties::prop_bool, false>>()
                 .func<&func_type::v, entt::as_void_t>("v"_hs)
                 .func<&func_type::a, entt::as_alias_t>("a"_hs);
 
@@ -209,13 +225,15 @@ struct Meta: ::testing::Test {
                 .data<&setter_getter_type::setter_with_ref, &setter_getter_type::getter_with_ref>("w"_hs);
 
         entt::meta<an_abstract_type>()
-                .type("an_abstract_type"_hs, std::make_pair(properties::prop_bool, false))
+                .type("an_abstract_type"_hs)
+                    .prop<entt::property<properties::prop_bool, false>>()
                 .data<&an_abstract_type::i>("i"_hs)
                 .func<&an_abstract_type::f>("f"_hs)
                 .func<&an_abstract_type::g>("g"_hs);
 
         entt::meta<another_abstract_type>()
-                .type("another_abstract_type"_hs, std::make_pair(properties::prop_int, 42))
+                .type("another_abstract_type"_hs)
+                    .prop<entt::property<properties::prop_int, 42>>()
                 .data<&another_abstract_type::j>("j"_hs)
                 .func<&another_abstract_type::h>("h"_hs);
 
@@ -230,7 +248,8 @@ struct Meta: ::testing::Test {
         entt::meta<double>().conv<float>();
 
         entt::meta<derived_type>()
-                .type("my_type"_hs, std::make_pair(properties::prop_bool, false))
+                .type("my_type"_hs)
+                    .prop<entt::property<properties::prop_bool, false>>()
                 .ctor<>();
 
         entt::meta<another_abstract_type>()
@@ -1982,6 +2001,18 @@ TEST_F(Meta, Variables) {
     ASSERT_EQ(c, 'x');
 }
 
+TEST_F(Meta, SharedProperties) {
+    const auto type = entt::resolve<properties>();
+    const auto prop_bool = type.data("prop_bool"_hs);
+    const auto prop_int = type.data("prop_int"_hs);
+
+    ASSERT_TRUE(prop_bool.prop(properties::prop_int));
+    ASSERT_FALSE(prop_bool.prop(properties::prop_bool));
+
+    ASSERT_TRUE(prop_int.prop(properties::prop_int));
+    ASSERT_TRUE(prop_int.prop(properties::prop_bool));
+}
+
 TEST_F(Meta, Reset) {
     ASSERT_NE(*entt::internal::meta_info<>::ctx, nullptr);
     ASSERT_NE(entt::internal::meta_info<>::local, nullptr);