Browse Source

meta: review overloaded meta functions

Michele Caini 5 years ago
parent
commit
92f79c99f7
4 changed files with 98 additions and 129 deletions
  1. 7 18
      src/entt/meta/factory.hpp
  2. 5 10
      src/entt/meta/internal.hpp
  3. 46 61
      src/entt/meta/meta.hpp
  4. 40 40
      test/entt/meta/meta_type.cpp

+ 7 - 18
src/entt/meta/factory.hpp

@@ -675,8 +675,8 @@ public:
         static internal::meta_func_node node{
             {},
             type,
-            nullptr, // parent
-            nullptr, // next
+            nullptr,
+            nullptr,
             std::tuple_size_v<typename helper_type::args_type>,
             helper_type::is_const,
             helper_type::is_static,
@@ -689,24 +689,13 @@ public:
 
         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;
-        }
+        internal::meta_func_node **it = &type->func;
+        for(; *it && (*it)->id != id; it = &(*it)->next);
+        for(; *it && (*it)->id == id && (*it)->size < node.size; it = &(*it)->next);
 
         node.id = id;
+        node.next = *it;
+        *it = &node;
 
         return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
     }

+ 5 - 10
src/entt/meta/internal.hpp

@@ -362,24 +362,19 @@ private:
 template<auto Member, typename Op>
 auto find_if(const Op &op, const meta_type_node *node)
 -> std::decay_t<decltype(node->*Member)> {
-    std::decay_t<decltype(node->*Member)> ret = nullptr;
-
     for(auto &&curr: meta_range{node->*Member}) {
         if(op(&curr)) {
-            ret = &curr;
-            break;
+            return &curr;
         }
     }
 
-    if(!ret) {
-        for(auto &&curr: meta_range{node->base}) {
-            if(ret = find_if<Member>(op, curr.type()); ret) {
-                break;
-            }
+    for(auto &&curr: meta_range{node->base}) {
+        if(auto *ret = find_if<Member>(op, curr.type()); ret) {
+            return ret;
         }
     }
 
-    return ret;
+    return nullptr;
 }
 
 

+ 46 - 61
src/entt/meta/meta.hpp

@@ -735,7 +735,7 @@ struct meta_ctor {
      * @param sz Number of parameters to use to construct the instance.
      * @return A meta any containing the new instance, if any.
      */
-    [[nodiscard]] meta_any invoke(meta_any * const args, const std::size_t sz) const {
+    [[nodiscard]] meta_any invoke(meta_any * const args, const size_type sz) const {
         return sz == size() ? node->invoke(args) : meta_any{};
     }
 
@@ -954,7 +954,7 @@ struct meta_func {
      * @param sz Number of parameters to use to invoke the function.
      * @return A meta any containing the returned value, if any.
      */
-    meta_any invoke(meta_handle instance, meta_any * const args, const std::size_t sz) const {
+    meta_any invoke(meta_handle instance, meta_any * const args, const size_type sz) const {
         return sz == size() ? node->invoke(std::move(instance), args) : meta_any{};
     }
 
@@ -1004,17 +1004,20 @@ private:
 
 /*! @brief Opaque wrapper for meta types. */
 class meta_type {
-    template<typename... Args, std::size_t... Indexes>
-    [[nodiscard]] auto ctor(std::index_sequence<Indexes...>) const {
-        internal::meta_range range{node->ctor};
+    bool can_cast_or_convert(const meta_type type, const type_info info) const ENTT_NOEXCEPT {
+        for(auto conv: type.conv()) {
+            if(conv.type().info() == info) {
+                return true;
+            }
+        }
+
+        for(auto base: type.base()) {
+            if(base.type().info() == info || can_cast_or_convert(base.type(), info)) {
+                return true;
+            }
+        }
 
-        return std::find_if(range.begin(), range.end(), [](const auto &candidate) {
-            return candidate.size == sizeof...(Args) && ([](auto *from, auto *to) {
-                return (from->info == to->info)
-                        || internal::find_if<&node_type::base>([to](const auto *curr) { return curr->type()->info == to->info; }, from)
-                        || internal::find_if<&node_type::conv>([to](const auto *curr) { return curr->type()->info == to->info; }, from);
-            }(internal::meta_info<Args>::resolve(), candidate.arg(Indexes)) && ...);
-        }).operator->();
+        return false;
     }
 
 public:
@@ -1268,7 +1271,13 @@ public:
      */
     template<typename... Args>
     [[nodiscard]] meta_ctor ctor() const {
-        return ctor<Args...>(std::index_sequence_for<Args...>{});
+        for(const auto &candidate: internal::meta_range{node->ctor}) {
+            if(size_type index{}; candidate.size == sizeof...(Args) && ([this](auto *from, auto *to) { return from->info == to->info || can_cast_or_convert(from, to->info); }(internal::meta_info<Args>::resolve(), candidate.arg(index++)) && ...)) {
+                return &candidate;
+            }
+        }
+
+        return nullptr;
     }
 
     /**
@@ -1304,7 +1313,9 @@ public:
     /**
      * @brief Returns the meta function associated with a given identifier.
      *
-     * The meta functions of the base classes will also be visited, if any.
+     * The meta functions of the base classes will also be visited, if any.<br/>
+     * In the case of overloaded meta functions, the first one with the required
+     * id will be returned.
      *
      * @param id Unique identifier.
      * @return The meta function associated with the given identifier, if any.
@@ -1326,7 +1337,7 @@ public:
      * @param sz Number of parameters to use to construct the instance.
      * @return A meta any containing the new instance, if any.
      */
-    [[nodiscard]] meta_any construct(meta_any * const args, const std::size_t sz) const {
+    [[nodiscard]] meta_any construct(meta_any * const args, const size_type sz) const {
         meta_any any{};
 
         internal::find_if<&node_type::ctor>([args, sz, &any](const auto *curr) {
@@ -1367,59 +1378,33 @@ public:
      * @param sz Number of parameters to use to invoke the function.
      * @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 {
-        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;
-            }
+    meta_any invoke(const id_type id, meta_handle instance, meta_any * const args, const size_type sz) const {
+        const internal::meta_func_node* candidate{};
+        size_type extent{sz + 1u};
+        bool ambiguous{};
 
-            node_type::size_type casts_required{ 0 };
-            bool match{ true };
+        for(auto *it = internal::find_if<&node_type::func>([id, sz](const auto *curr) { return curr->id == id && curr->size == sz; }, node); it && it->id == id && it->size == sz; it = it->next) {
+            size_type direct{};
+            size_type ext{};
 
-            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;
+            for(size_type next{}; next < sz && next == (direct + ext); ++next) {
+                const auto type = args[next].type();
+                const auto req = it->arg(next)->info;
+                type.info() == req ? ++direct : (ext += can_cast_or_convert(type, req));
             }
 
-            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;
+            if((direct + ext) == sz) {
+                if(ext < extent) {
+                    candidate = it;
+                    extent = ext;
+                    ambiguous = false;
+                } else if(ext == extent) {
+                    ambiguous = true;
+                }
             }
         }
 
-        return best_match ? best_match->invoke (instance, args) : meta_any{};
+        return (candidate && !ambiguous) ? candidate->invoke(instance, args) : meta_any{};
     }
 
     /**

+ 40 - 40
test/entt/meta/meta_type.cpp

@@ -49,7 +49,7 @@ struct clazz_t {
 };
 
 struct overloaded_func_t {
-    void e (int v) {
+    void e(int v) {
         value = v + v;
     }
 
@@ -59,19 +59,19 @@ struct overloaded_func_t {
 
     int f(int a, int b) {
         value = a;
-        return b*b;
+        return b * b;
     }
 
     int f(int v) const {
-        return v*v;
+        return v * v;
     }
 
-    float f (int a, float b) {
+    float f(int a, float b) {
         value = a;
         return b + b;
     }
 
-    void g (int v) {
+    void g(int v) {
         value = v * v;
     }
 
@@ -100,12 +100,12 @@ struct MetaType: ::testing::Test {
         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);
+            .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>()
             .data<property_t::random>("random"_hs)
@@ -326,54 +326,54 @@ TEST_F(MetaType, Invoke) {
 }
 
 TEST_F(MetaType, OverloadedFunc) {
-    entt::meta<float> ().conv<int>();
+    entt::meta<float>().conv<int>();
     entt::meta<double>().conv<float>();
 
-    auto type = entt::resolve<overloaded_func_t>();
+    const 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);
+    const auto first = 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);
+    ASSERT_TRUE(first);
+    ASSERT_EQ(overloaded_func_t::value, 1);
+    ASSERT_NE(first.try_cast<int>(), nullptr);
+    ASSERT_EQ(first.cast<int>(), 4);
 
-    auto const second_overload_result = type.invoke("f"_hs, instance, 3, 4);
+    const auto second = 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);
+    ASSERT_TRUE(second);
+    ASSERT_EQ(overloaded_func_t::value, 3);
+    ASSERT_NE(second.try_cast<int>(), nullptr);
+    ASSERT_EQ(second.cast<int>(), 16);
 
-    auto const third_overload_result = type.invoke("f"_hs, instance, 5);
+    const auto third = 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);
+    ASSERT_TRUE(third);
+    ASSERT_EQ(overloaded_func_t::value, 3);
+    ASSERT_NE(third.try_cast<int>(), nullptr);
+    ASSERT_EQ(third.cast<int>(), 25);
 
-    auto const fourth_overload_result = type.invoke("f"_hs, instance, 6, 7.0f);
+    const auto fourth = type.invoke("f"_hs, instance, 6, 7.f);
 
-    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);
+    ASSERT_TRUE(fourth);
+    ASSERT_EQ(overloaded_func_t::value, 6);
+    ASSERT_NE(fourth.try_cast<float>(), nullptr);
+    ASSERT_EQ(fourth.cast<float>(), 14.f);
 
-    auto const overload_with_cast_result = type.invoke("f"_hs, instance, 8, 9.0f);
+    const auto cast = type.invoke("f"_hs, instance, 8, 9.f);
 
-    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);
+    ASSERT_TRUE(cast);
+    ASSERT_EQ(overloaded_func_t::value, 8);
+    ASSERT_NE(cast.try_cast<float>(), nullptr);
+    ASSERT_EQ(cast.cast<float>(), 18.f);
 
-    auto const ambiguous_overload_result = type.invoke("f"_hs, instance, 8, 9.0);
+    const auto ambiguous = type.invoke("f"_hs, instance, 8, 9.);
 
-    ASSERT_FALSE (ambiguous_overload_result);
+    ASSERT_FALSE(ambiguous);
 }
 
 TEST_F(MetaType, SetGet) {