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

[meta] Meta-function overload support (#538)

Now meta-functions can be overloaded. When a function with a duplicate
identifier is registered, it's added into the meta-type function list after the
first meta-function with the same id. During the invocation, the meta-functions
with the requested id are iterated, and the best matching one is chosen. The
overload that doesn't require any type conversions or the minimal number of
those is considered the best match. If none of the overloads matches the given
arguments or if there are multiple matching overloads requiring the same number
of type casts then the invocation will fail the same way it happens in C++.

Signed-off-by: Innokentiy Alaytsev <alaitsev@gmail.com>
Innokentiy Alaytsev пре 5 година
родитељ
комит
24f937fe4f
3 измењених фајлова са 162 додато и 7 уклоњено
  1. 20 5
      src/entt/meta/factory.hpp
  2. 52 2
      src/entt/meta/meta.hpp
  3. 90 0
      test/entt/meta/meta_type.cpp

+ 20 - 5
src/entt/meta/factory.hpp

@@ -675,8 +675,8 @@ public:
         static internal::meta_func_node node{
         static internal::meta_func_node node{
             {},
             {},
             type,
             type,
-            nullptr,
-            nullptr,
+            nullptr, // parent
+            nullptr, // next
             std::tuple_size_v<typename helper_type::args_type>,
             std::tuple_size_v<typename helper_type::args_type>,
             helper_type::is_const,
             helper_type::is_const,
             helper_type::is_static,
             helper_type::is_static,
@@ -687,11 +687,26 @@ public:
             }
             }
         };
         };
 
 
-        ENTT_ASSERT(!exists(id, type->func));
         ENTT_ASSERT(!exists(&node, type->func));
         ENTT_ASSERT(!exists(&node, type->func));
+
+        if (exists(id, type->func)) {
+            auto const existing_overload =
+                internal::find_if<&std::decay_t<decltype(*type)>::func>(
+                    [id](const auto* curr) {
+                        return id == curr->id;
+                    }, type);
+
+            ENTT_ASSERT(existing_overload);
+
+            node.next = existing_overload->next;
+            existing_overload->next = &node;
+        }
+        else {
+            node.next = type->func;
+            type->func = &node;
+        }
+
         node.id = id;
         node.id = id;
-        node.next = type->func;
-        type->func = &node;
 
 
         return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
         return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
     }
     }

+ 52 - 2
src/entt/meta/meta.hpp

@@ -1368,8 +1368,58 @@ public:
      * @return A meta any containing the returned value, if any.
      * @return A meta any containing the returned value, if any.
      */
      */
     meta_any invoke(const id_type id, meta_handle instance, meta_any * const args, const std::size_t sz) const {
     meta_any invoke(const id_type id, meta_handle instance, meta_any * const args, const std::size_t sz) const {
-        auto const candidate = func(id);
-        return candidate ? candidate.invoke(std::move(instance), args, sz) : meta_any{};
+        const internal::meta_func_node* best_match{ nullptr };
+        internal::meta_func_node::size_type best_match_casts_required{0};
+
+        for (auto candidate = internal::find_if<&node_type::func>([id](const auto *curr) { return curr->id == id; }, node); candidate && (id == candidate->id); candidate = candidate->next) {
+            if (sz != candidate->size) {
+                continue;
+            }
+
+            node_type::size_type casts_required{ 0 };
+            bool match{ true };
+
+            for (node_type::size_type arg_idx = 0; arg_idx < sz; ++arg_idx) {
+                auto const& candidate_arg_type_info = candidate->arg (arg_idx)->info;
+                auto const arg_type = (args + arg_idx)->type();
+
+				ENTT_ASSERT(bool(candidate_arg_type_info));
+
+                if (candidate_arg_type_info == arg_type.info()) {
+                    continue;
+                }
+
+                if (auto const arg_convs = arg_type.conv();
+                    arg_convs.end () != std::find_if(
+                        arg_convs.begin(),
+                        arg_convs.end(),
+                        [candidate_arg_type_info](auto&& curr) {
+                            return curr.type().info() == candidate_arg_type_info;
+                        })) {
+                    casts_required++;
+                }
+                else {
+                    match = false;
+                    break;
+                }
+
+            }
+
+            if (!match) {
+                continue;
+            }
+
+            if ((0 < best_match_casts_required) && (casts_required == best_match_casts_required)) {
+                return meta_any{};
+            }
+
+            if (!best_match || (casts_required < best_match_casts_required)) {
+                best_match = candidate;
+                best_match_casts_required = casts_required;
+            }
+        }
+
+        return best_match ? best_match->invoke (instance, args) : meta_any{};
     }
     }
 
 
     /**
     /**

+ 90 - 0
test/entt/meta/meta_type.cpp

@@ -48,6 +48,36 @@ struct clazz_t {
     int value;
     int value;
 };
 };
 
 
+struct overloaded_func_t {
+    void e (int v) {
+        value = v + v;
+    }
+
+    int f(const base_t &, int a, int b) {
+        return f(a, b);
+    }
+
+    int f(int a, int b) {
+        value = a;
+        return b*b;
+    }
+
+    int f(int v) const {
+        return v*v;
+    }
+
+    float f (int a, float b) {
+        value = a;
+        return b + b;
+    }
+
+    void g (int v) {
+        value = v * v;
+    }
+
+    inline static int value = 0;
+};
+
 enum class property_t {
 enum class property_t {
     random,
     random,
     value,
     value,
@@ -69,6 +99,14 @@ struct MetaType: ::testing::Test {
         entt::meta<abstract_t>().func<&abstract_t::func>("func"_hs);
         entt::meta<abstract_t>().func<&abstract_t::func>("func"_hs);
         entt::meta<concrete_t>().base<base_t>().base<abstract_t>();
         entt::meta<concrete_t>().base<base_t>().base<abstract_t>();
 
 
+        entt::meta<overloaded_func_t>().type("overloaded_func"_hs)
+                .func<&overloaded_func_t::e> ("e"_hs)
+                .func<entt::overload<int(const base_t &, int, int)>(&overloaded_func_t::f)>("f"_hs)
+                .func<entt::overload<int(int, int)>(&overloaded_func_t::f)>("f"_hs)
+                .func<entt::overload<int(int) const>(&overloaded_func_t::f)>("f"_hs)
+                .func<entt::overload<float (int, float)> (&overloaded_func_t::f)> ("f"_hs)
+                .func<&overloaded_func_t::g> ("g"_hs);
+
         entt::meta<property_t>()
         entt::meta<property_t>()
             .data<property_t::random>("random"_hs)
             .data<property_t::random>("random"_hs)
                 .prop(property_t::random, 0)
                 .prop(property_t::random, 0)
@@ -287,6 +325,57 @@ TEST_F(MetaType, Invoke) {
     ASSERT_FALSE(type.invoke("rebmem"_hs, {}));
     ASSERT_FALSE(type.invoke("rebmem"_hs, {}));
 }
 }
 
 
+TEST_F(MetaType, OverloadedFunc) {
+    entt::meta<float> ().conv<int>();
+    entt::meta<double>().conv<float>();
+
+    auto type = entt::resolve<overloaded_func_t>();
+    overloaded_func_t instance{};
+
+    ASSERT_TRUE(type.func("f"_hs));
+    ASSERT_TRUE(type.func("e"_hs));
+    ASSERT_TRUE(type.func("g"_hs));
+
+    auto const first_overload_result = type.invoke("f"_hs, instance, base_t{}, 1, 2);
+
+    ASSERT_TRUE(first_overload_result);
+    ASSERT_EQ (overloaded_func_t::value, 1);
+    ASSERT_TRUE (first_overload_result.try_cast<int>());
+    ASSERT_EQ (first_overload_result.cast<int>(), 4);
+
+    auto const second_overload_result = type.invoke("f"_hs, instance, 3, 4);
+
+    ASSERT_TRUE (second_overload_result);
+    ASSERT_EQ (overloaded_func_t::value, 3);
+    ASSERT_TRUE (second_overload_result.try_cast<int>());
+    ASSERT_EQ (second_overload_result.cast<int> (), 16);
+
+    auto const third_overload_result = type.invoke("f"_hs, instance, 5);
+
+    ASSERT_TRUE (third_overload_result);
+    ASSERT_EQ (overloaded_func_t::value, 3);
+    ASSERT_TRUE (third_overload_result.try_cast<int>());
+    ASSERT_EQ (third_overload_result.cast<int> (), 25);
+
+    auto const fourth_overload_result = type.invoke("f"_hs, instance, 6, 7.0f);
+
+    ASSERT_TRUE (fourth_overload_result);
+    ASSERT_EQ (overloaded_func_t::value, 6);
+    ASSERT_TRUE (fourth_overload_result.try_cast<float>());
+    ASSERT_EQ (fourth_overload_result.cast<float> (), 14.0f);
+
+    auto const overload_with_cast_result = type.invoke("f"_hs, instance, 8, 9.0f);
+
+    ASSERT_TRUE (overload_with_cast_result);
+    ASSERT_EQ (overloaded_func_t::value, 8);
+    ASSERT_TRUE (overload_with_cast_result.try_cast<float>());
+    ASSERT_EQ (overload_with_cast_result.cast<float> (), 18);
+
+    auto const ambiguous_overload_result = type.invoke("f"_hs, instance, 8, 9.0);
+
+    ASSERT_FALSE (ambiguous_overload_result);
+}
+
 TEST_F(MetaType, SetGet) {
 TEST_F(MetaType, SetGet) {
     auto type = entt::resolve<clazz_t>();
     auto type = entt::resolve<clazz_t>();
     clazz_t instance{};
     clazz_t instance{};
@@ -442,6 +531,7 @@ TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
     entt::resolve<derived_t>().reset();
     entt::resolve<derived_t>().reset();
     entt::resolve<abstract_t>().reset();
     entt::resolve<abstract_t>().reset();
     entt::resolve<concrete_t>().reset();
     entt::resolve<concrete_t>().reset();
+    entt::resolve<overloaded_func_t> ().reset ();
     entt::resolve<property_t>().reset();
     entt::resolve<property_t>().reset();
     entt::resolve<clazz_t>().reset();
     entt::resolve<clazz_t>().reset();