Răsfoiți Sursa

meta: support for setters/getters

Michele Caini 7 ani în urmă
părinte
comite
66e7141415
4 a modificat fișierele cu 160 adăugiri și 16 ștergeri
  1. 5 1
      docs/meta.md
  2. 51 2
      src/entt/meta/factory.hpp
  3. 41 13
      src/entt/meta/meta.hpp
  4. 63 0
      test/entt/meta/meta.cpp

+ 5 - 1
docs/meta.md

@@ -85,7 +85,11 @@ It can be used to extend the reflected type and add the following:
 
 
   This function requires as an argument the name to give to the meta data once
   This function requires as an argument the name to give to the meta data once
   created. Users can then access meta data at runtime by searching for them by
   created. Users can then access meta data at runtime by searching for them by
-  name.
+  name.<br/>
+  Data members can be set also by means of a couple of functions, namely a
+  setter and a getter. Setters and getters can be either free functions, member
+  functions or mixed ones, as long as they respect the required signatures.<br/>
+  Refer to the inline documentation for all the details.
 
 
 * _Member functions_. Both real member functions of the underlying type and free
 * _Member functions_. Both real member functions of the underlying type and free
   functions can be attached to a meta type. From a client's point of view, all
   functions can be attached to a meta type. From a client's point of view, all

+ 51 - 2
src/entt/meta/factory.hpp

@@ -281,8 +281,8 @@ public:
      * @return A meta factory for the parent type.
      * @return A meta factory for the parent type.
      */
      */
     template<auto *Func>
     template<auto *Func>
-    std::enable_if_t<std::is_invocable_v<decltype(Func), Type &>, meta_factory &>
-    dtor() ENTT_NOEXCEPT {
+    meta_factory & dtor() ENTT_NOEXCEPT {
+        static_assert(std::is_invocable_v<decltype(Func), Type &>);
         auto * const type = internal::meta_info<Type>::resolve();
         auto * const type = internal::meta_info<Type>::resolve();
 
 
         static internal::meta_dtor_node node{
         static internal::meta_dtor_node node{
@@ -368,6 +368,55 @@ public:
         return *this;
         return *this;
     }
     }
 
 
+    /**
+     * @brief Assigns a meta data to a meta type by means of its setter and
+     * getter.
+     *
+     * Setters and getters can be either free functions, member functions or a
+     * mix of them.<br/>
+     * In case of free functions, setters and getters must accept an instance of
+     * the parent type as their first argument. A setter has then an extra
+     * argument of a type convertible to that of the parameter to set.<br/>
+     * In case of member functions, getters have no arguments at all, while
+     * setters has an argument of a type convertible to that of the parameter to
+     * set.
+     *
+     * @tparam Setter The actual function to use as a setter.
+     * @tparam Getter The actual function to use as a getter.
+     * @tparam Property Types of properties to assign to the meta data.
+     * @param str The name to assign to the meta data.
+     * @param property Properties to assign to the meta data.
+     * @return A meta factory for the parent type.
+     */
+    template<auto Setter, auto Getter, typename... Property>
+    meta_factory & data(const char *str, Property &&... property) ENTT_NOEXCEPT {
+        using data_type = std::invoke_result_t<decltype(Getter), Type &>;
+        static_assert(std::is_invocable_v<decltype(Setter), Type &, data_type>);
+        auto * const type = internal::meta_info<Type>::resolve();
+
+        static internal::meta_data_node node{
+            hashed_string{str},
+            type->data,
+            type,
+            properties<std::tuple<decltype(Setter), decltype(Getter)>>(std::forward<Property>(property)...),
+            false,
+            false,
+            &internal::meta_info<data_type>::resolve,
+            &internal::setter<false, Type, Setter>,
+            &internal::getter<Type, Getter>,
+            []() -> meta_data {
+                return &node;
+            }
+        };
+
+        assert(!duplicate(hashed_string{str}, node.next));
+        assert((!internal::meta_info<Type>::template data<Setter, Getter>));
+        internal::meta_info<Type>::template data<Setter, Getter> = &node;
+        type->data = &node;
+
+        return *this;
+    }
+
     /**
     /**
      * @brief Assigns a meta funcion to a meta type.
      * @brief Assigns a meta funcion to a meta type.
      *
      *

+ 41 - 13
src/entt/meta/meta.hpp

@@ -163,7 +163,7 @@ struct meta_node<Type> {
     template<auto>
     template<auto>
     inline static meta_dtor_node *dtor = nullptr;
     inline static meta_dtor_node *dtor = nullptr;
 
 
-    template<auto>
+    template<auto...>
     inline static meta_data_node *data = nullptr;
     inline static meta_data_node *data = nullptr;
 
 
     template<auto>
     template<auto>
@@ -2056,18 +2056,38 @@ template<bool Const, typename Type, auto Data>
 bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any &any) {
 bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any &any) {
     if constexpr(Const) {
     if constexpr(Const) {
         return false;
         return false;
-    } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-        using data_type = std::decay_t<decltype(std::declval<Type>().*Data)>;
-        static_assert(std::is_invocable_v<decltype(Data), Type>);
-        const bool accepted = any.can_cast<data_type>() || any.convert<data_type>();
-        auto *clazz = handle.try_cast<Type>();
+    } else if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>> || std::is_member_pointer_v<decltype(Data)>) {
+        auto execute = [&handle, &any](auto *tag) {
+            using data_type = std::remove_pointer_t<decltype(tag)>;
+            const bool accepted = any.can_cast<data_type>() || any.convert<data_type>();
+            auto *clazz = handle.try_cast<Type>();
+
+            if(accepted && clazz) {
+                if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>>) {
+                    Data(*clazz, any.cast<data_type>());
+                } else if constexpr(std::is_member_function_pointer_v<decltype(Data)>) {
+                    (clazz->*Data)(any.cast<data_type>());
+                } else /* if constexpr(std::is_member_object_pointer_v<decltype(Data)>) */ {
+                    clazz->*Data = any.cast<data_type>();
+                }
+            }
 
 
-        if(accepted && clazz) {
-            clazz->*Data = any.cast<data_type>();
-        }
+            return accepted;
+        };
 
 
-        return accepted;
+        if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>> || std::is_member_function_pointer_v<decltype(Data)>) {
+            using helper_type = meta_function_helper<std::integral_constant<decltype(Data), Data>>;
+            using data_type = std::tuple_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>;
+            data_type *tag = nullptr;
+            return execute(tag);
+        } else /* if constexpr(std::is_member_object_pointer_v<decltype(Data)>) */ {
+            using data_type = std::decay_t<decltype(std::declval<Type>().*Data)>;
+            static_assert(std::is_invocable_v<decltype(Data), Type>);
+            data_type *tag = nullptr;
+            return execute(tag);
+        }
     } else {
     } else {
+        static_assert(std::is_pointer_v<decltype(Data)>);
         using data_type = std::decay_t<decltype(*Data)>;
         using data_type = std::decay_t<decltype(*Data)>;
         const bool accepted = any.can_cast<data_type>() || any.convert<data_type>();
         const bool accepted = any.can_cast<data_type>() || any.convert<data_type>();
 
 
@@ -2082,11 +2102,19 @@ bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any &any)
 
 
 template<typename Type, auto Data>
 template<typename Type, auto Data>
 inline meta_any getter([[maybe_unused]] meta_handle handle) {
 inline meta_any getter([[maybe_unused]] meta_handle handle) {
-    if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-        static_assert(std::is_invocable_v<decltype(Data), Type>);
+    if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>> || std::is_member_pointer_v<decltype(Data)>) {
+        static_assert(std::is_invocable_v<decltype(Data), Type &>);
         auto *clazz = handle.try_cast<Type>();
         auto *clazz = handle.try_cast<Type>();
-        return clazz ? meta_any{clazz->*Data} : meta_any{};
+
+        if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>>) {
+            return clazz ? meta_any{Data(*clazz)} : meta_any{};
+        } else if constexpr(std::is_member_function_pointer_v<decltype(Data)>) {
+            return clazz ? meta_any{(clazz->*Data)()} : meta_any{};
+        } else /* if constexpr(std::is_member_object_pointer_v<decltype(Data)>) */ {
+            return clazz ? meta_any{clazz->*Data} : meta_any{};
+        }
     } else {
     } else {
+        static_assert(std::is_pointer_v<decltype(Data)>);
         return meta_any{*Data};
         return meta_any{*Data};
     }
     }
 }
 }

+ 63 - 0
test/entt/meta/meta.cpp

@@ -80,6 +80,16 @@ struct func_type {
     inline static int value = 0;
     inline static int value = 0;
 };
 };
 
 
+struct setter_getter_type {
+    int value{};
+
+    int setter(int value) { return this->value = value; }
+    int getter() { return value; }
+
+    static int static_setter(setter_getter_type &type, int value) { return type.value = value; }
+    static int static_getter(const setter_getter_type &type) { return type.value; }
+};
+
 struct not_comparable_type {
 struct not_comparable_type {
     bool operator==(const not_comparable_type &) const = delete;
     bool operator==(const not_comparable_type &) const = delete;
 };
 };
@@ -146,6 +156,11 @@ struct Meta: public ::testing::Test {
                 .func<&func_type::h>("h", std::make_pair(properties::prop_bool, false))
                 .func<&func_type::h>("h", std::make_pair(properties::prop_bool, false))
                 .func<&func_type::k>("k", std::make_pair(properties::prop_bool, false));
                 .func<&func_type::k>("k", std::make_pair(properties::prop_bool, false));
 
 
+        entt::reflect<setter_getter_type>("setter_getter")
+                .data<&setter_getter_type::static_setter, &setter_getter_type::static_getter>("x")
+                .data<&setter_getter_type::setter, &setter_getter_type::getter>("y")
+                .data<&setter_getter_type::static_setter, &setter_getter_type::getter>("z");
+
         entt::reflect<an_abstract_type>("an_abstract_type", std::make_pair(properties::prop_bool, false))
         entt::reflect<an_abstract_type>("an_abstract_type", std::make_pair(properties::prop_bool, false))
                 .data<&an_abstract_type::i>("i")
                 .data<&an_abstract_type::i>("i")
                 .func<&an_abstract_type::f>("f")
                 .func<&an_abstract_type::f>("f")
@@ -884,6 +899,54 @@ TEST_F(Meta, MetaDataSetConvert) {
     ASSERT_EQ(instance.i, 3);
     ASSERT_EQ(instance.i, 3);
 }
 }
 
 
+TEST_F(Meta, MetaDataSetterGetterAsFreeFunctions) {
+    auto data = entt::resolve<setter_getter_type>().data("x");
+    setter_getter_type instance{};
+
+    ASSERT_TRUE(data);
+    ASSERT_NE(data, entt::meta_data{});
+    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"));
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_STREQ(data.name(), "x");
+    ASSERT_FALSE(data.is_const());
+    ASSERT_FALSE(data.is_static());
+    ASSERT_EQ(data.get(instance).cast<int>(), 0);
+    ASSERT_TRUE(data.set(instance, 42));
+    ASSERT_EQ(data.get(instance).cast<int>(), 42);
+}
+
+TEST_F(Meta, MetaDataSetterGetterAsMemberFunctions) {
+    auto data = entt::resolve<setter_getter_type>().data("y");
+    setter_getter_type instance{};
+
+    ASSERT_TRUE(data);
+    ASSERT_NE(data, entt::meta_data{});
+    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"));
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_STREQ(data.name(), "y");
+    ASSERT_FALSE(data.is_const());
+    ASSERT_FALSE(data.is_static());
+    ASSERT_EQ(data.get(instance).cast<int>(), 0);
+    ASSERT_TRUE(data.set(instance, 42));
+    ASSERT_EQ(data.get(instance).cast<int>(), 42);
+}
+
+TEST_F(Meta, MetaDataSetterGetterMixed) {
+    auto data = entt::resolve<setter_getter_type>().data("z");
+    setter_getter_type instance{};
+
+    ASSERT_TRUE(data);
+    ASSERT_NE(data, entt::meta_data{});
+    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"));
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_STREQ(data.name(), "z");
+    ASSERT_FALSE(data.is_const());
+    ASSERT_FALSE(data.is_static());
+    ASSERT_EQ(data.get(instance).cast<int>(), 0);
+    ASSERT_TRUE(data.set(instance, 42));
+    ASSERT_EQ(data.get(instance).cast<int>(), 42);
+}
+
 TEST_F(Meta, MetaFunc) {
 TEST_F(Meta, MetaFunc) {
     auto func = entt::resolve<func_type>().func("f2");
     auto func = entt::resolve<func_type>().func("f2");
     func_type instance{};
     func_type instance{};