Răsfoiți Sursa

meta: support for multi-setters on meta data members

Michele Caini 4 ani în urmă
părinte
comite
42dcec961b
3 a modificat fișierele cu 97 adăugiri și 2 ștergeri
  1. 6 0
      docs/md/meta.md
  2. 46 2
      src/entt/meta/factory.hpp
  3. 45 0
      test/entt/meta/meta_data.cpp

+ 6 - 0
docs/md/meta.md

@@ -147,6 +147,12 @@ decorated version of it. This object can be used to add the following:
   entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
   ```
 
+  Multiple setters are also supported by means of a `value_list` object:
+
+  ```cpp
+  entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs);
+  ```
+
   Refer to the inline documentation for all the details.
 
 * _Member functions_. Both real member functions of the underlying type and free

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

@@ -182,6 +182,28 @@ class meta_factory<Type> {
         }
     }
 
+    template<typename Setter, auto Getter, typename Policy, auto... Index>
+    auto data(const id_type id, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+        using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Getter), Type &>>;
+        using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
+
+        static internal::meta_data_node node{
+            {},
+            nullptr,
+            nullptr,
+            Setter::size,
+            /* this is never static */
+            (std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<data_type>) ? internal::meta_traits::IS_CONST : internal::meta_traits::IS_NONE,
+            internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
+            &meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
+            [](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value) || ...); },
+            &meta_getter<Type, Getter, Policy>
+        };
+
+        link_data_if_required(id, node);
+        return meta_factory<Type, Setter, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
+    }
+
 public:
     /*! @brief Default constructor. */
     meta_factory()
@@ -455,7 +477,7 @@ public:
             link_data_if_required(id, node);
             return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
         } else {
-            using descriptor = meta_function_helper_t<Type, decltype(Setter)>;
+            using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
 
             static internal::meta_data_node node{
                 {},
@@ -465,7 +487,7 @@ public:
                 /* this is never static */
                 (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<data_type>) ? internal::meta_traits::IS_CONST : internal::meta_traits::IS_NONE,
                 internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
-                &meta_arg<type_list<type_list_element_t<descriptor::args_type::size != 1u, typename descriptor::args_type>>>,
+                &meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
                 &meta_setter<Type, Setter>,
                 &meta_getter<Type, Getter, Policy>
             };
@@ -475,6 +497,28 @@ public:
         }
     }
 
+    /**
+     * @brief Assigns a meta data to a meta type by means of its setters and
+     * getter.
+     *
+     * Multi-setter support for meta data members. All setters are tried in the
+     * order of definition before returning to the caller.<br/>
+     * Setters can be either free functions, member functions or a mix of them
+     * and are provided via a `value_list` type.
+     *
+     * @sa data
+     *
+     * @tparam Setter The actual functions to use as setters.
+     * @tparam Getter The actual getter function.
+     * @tparam Policy Optional policy (no policy set by default).
+     * @param id Unique identifier.
+     * @return An extended meta factory for the parent type.
+     */
+    template<typename Setter, auto Getter, typename Policy = as_is_t>
+    auto data(const id_type id) ENTT_NOEXCEPT {
+        return data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
+    }
+
     /**
      * @brief Assigns a meta funcion to a meta type.
      *

+ 45 - 0
test/entt/meta/meta_data.cpp

@@ -1,3 +1,5 @@
+#include <cstdlib>
+#include <string>
 #include <gtest/gtest.h>
 #include <entt/core/hashed_string.hpp>
 #include <entt/meta/factory.hpp>
@@ -65,6 +67,20 @@ struct setter_getter_t {
     int value;
 };
 
+struct multi_setter_t {
+    multi_setter_t(): value{0} {}
+
+    void from_double(double val) {
+        value = val;
+    }
+
+    void from_string(const char *val) {
+        value = std::atoi(val);
+    }
+
+    int value;
+};
+
 struct array_t {
     static inline int global[3];
     int local[5];
@@ -112,6 +128,10 @@ struct MetaData: ::testing::Test {
             .data<nullptr, &setter_getter_t::getter>("z_ro"_hs)
             .data<nullptr, &setter_getter_t::value>("value"_hs);
 
+        entt::meta<multi_setter_t>()
+            .type("multi_setter"_hs)
+            .data<entt::value_list<&multi_setter_t::from_double, &multi_setter_t::from_string>, &multi_setter_t::value>("value"_hs);
+
         entt::meta<array_t>()
             .type("array"_hs)
             .data<&array_t::global>("global"_hs)
@@ -452,6 +472,31 @@ TEST_F(MetaData, SetterGetterReadOnlyDataMember) {
     ASSERT_EQ(data.get(instance).cast<int>(), 0);
 }
 
+TEST_F(MetaData, MultiSetter) {
+    using namespace entt::literals;
+
+    auto data = entt::resolve<multi_setter_t>().data("value"_hs);
+    multi_setter_t instance{};
+
+    ASSERT_TRUE(data);
+    ASSERT_EQ(data.arity(), 2u);
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_EQ(data.arg(0u), entt::resolve<double>());
+    ASSERT_EQ(data.arg(1u), entt::resolve<const char *>());
+    ASSERT_EQ(data.arg(2u), entt::meta_type{});
+    ASSERT_EQ(data.id(), "value"_hs);
+    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);
+    ASSERT_TRUE(data.set(instance, 3.));
+    ASSERT_EQ(data.get(instance).cast<int>(), 3);
+    ASSERT_FALSE(data.set(instance, std::string{"99"}));
+    ASSERT_TRUE(data.set(instance, std::string{"99"}.c_str()));
+    ASSERT_EQ(data.get(instance).cast<int>(), 99);
+}
+
 TEST_F(MetaData, ConstInstance) {
     using namespace entt::literals;