Browse Source

meta: support const/non-const overloads of the same function

Michele Caini 3 years ago
parent
commit
fea0210b6e
2 changed files with 65 additions and 38 deletions
  1. 29 15
      src/entt/meta/meta.hpp
  2. 36 23
      test/entt/meta/meta_type.cpp

+ 29 - 15
src/entt/meta/meta.hpp

@@ -915,34 +915,48 @@ private:
 /*! @brief Opaque wrapper for types. */
 class meta_type {
     template<typename Func>
-    [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Func next) const {
+    [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const {
         decltype(next()) candidate = nullptr;
-        size_type extent{sz + 1u};
+        size_type same{};
         bool ambiguous{};
 
         for(auto curr = next(); curr; curr = next()) {
+            if constexpr(std::is_same_v<std::decay_t<decltype(*curr)>, internal::meta_func_node>) {
+                if(constness && !static_cast<bool>(curr->traits & internal::meta_traits::is_const)) {
+                    continue;
+                }
+            }
+
             if(curr->arity == sz) {
-                size_type direct{};
-                size_type ext{};
+                size_type match{};
+                size_type pos{};
 
-                for(size_type pos{}; pos < sz && pos == (direct + ext) && args[pos]; ++pos) {
+                for(; pos < sz && args[pos]; ++pos) {
                     const auto type = args[pos].type();
                     const auto other = curr->arg(pos);
 
                     if(const auto &info = other.info(); info == type.info()) {
-                        ++direct;
-                    } else {
-                        ext += (type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash())))
-                               || (type.node.conversion_helper && other.node.conversion_helper);
+                        ++match;
+                    } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash())))
+                                || (type.node.conversion_helper && other.node.conversion_helper))) {
+                        break;
                     }
                 }
 
-                if((direct + ext) == sz) {
-                    if(ext < extent) {
+                if(pos == sz) {
+                    if(!candidate || match > same) {
                         candidate = curr;
-                        extent = ext;
+                        same = match;
                         ambiguous = false;
-                    } else if(ext == extent) {
+                    } else if(match == same) {
+                        if constexpr(std::is_same_v<std::decay_t<decltype(*curr)>, internal::meta_func_node>) {
+                            if(static_cast<bool>(curr->traits & internal::meta_traits::is_const) != static_cast<bool>(candidate->traits & internal::meta_traits::is_const)) {
+                                candidate = static_cast<bool>(candidate->traits & internal::meta_traits::is_const) ? curr : candidate;
+                                ambiguous = false;
+                                continue;
+                            }
+                        }
+
                         ambiguous = true;
                     }
                 }
@@ -1216,7 +1230,7 @@ public:
      */
     [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const {
         if(node.details) {
-            const auto *candidate = lookup(args, sz, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable {
+            const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable {
                 return first == last ? nullptr : &(first++)->second;
             });
 
@@ -1278,7 +1292,7 @@ public:
     meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const {
         if(node.details) {
             if(auto it = node.details->func.find(id); it != node.details->func.cend()) {
-                const auto *candidate = lookup(args, sz, [curr = &it->second]() mutable {
+                const auto *candidate = lookup(args, sz, (instance->data() == nullptr), [curr = &it->second]() mutable {
                     return curr ? std::exchange(curr, curr->next.get()) : nullptr;
                 });
 

+ 36 - 23
test/entt/meta/meta_type.cpp

@@ -1,6 +1,7 @@
 #include <algorithm>
 #include <map>
 #include <memory>
+#include <type_traits>
 #include <utility>
 #include <vector>
 #include <gtest/gtest.h>
@@ -85,6 +86,10 @@ struct overloaded_func_t {
         return g(b);
     }
 
+    int f(int v) {
+        return 2 * std::as_const(*this).f(v);
+    }
+
     int f(int v) const {
         return g(v);
     }
@@ -144,6 +149,7 @@ struct MetaType: ::testing::Test {
             .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)>(&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);
@@ -390,49 +396,56 @@ TEST_F(MetaType, OverloadedFunc) {
 
     const auto type = entt::resolve<overloaded_func_t>();
     overloaded_func_t instance{};
+    entt::meta_any res{};
 
     ASSERT_TRUE(type.func("f"_hs));
     ASSERT_TRUE(type.func("e"_hs));
     ASSERT_TRUE(type.func("g"_hs));
 
-    const auto first = type.invoke("f"_hs, instance, base_t{}, 1, 2);
+    res = type.invoke("f"_hs, instance, base_t{}, 1, 2);
 
-    ASSERT_TRUE(first);
+    ASSERT_TRUE(res);
     ASSERT_EQ(overloaded_func_t::value, 1);
-    ASSERT_NE(first.try_cast<int>(), nullptr);
-    ASSERT_EQ(first.cast<int>(), 4);
+    ASSERT_NE(res.try_cast<int>(), nullptr);
+    ASSERT_EQ(res.cast<int>(), 4);
+
+    res = type.invoke("f"_hs, instance, 3, 4);
 
-    const auto second = type.invoke("f"_hs, instance, 3, 4);
+    ASSERT_TRUE(res);
+    ASSERT_EQ(overloaded_func_t::value, 3);
+    ASSERT_NE(res.try_cast<int>(), nullptr);
+    ASSERT_EQ(res.cast<int>(), 16);
+
+    res = type.invoke("f"_hs, instance, 5);
 
-    ASSERT_TRUE(second);
+    ASSERT_TRUE(res);
     ASSERT_EQ(overloaded_func_t::value, 3);
-    ASSERT_NE(second.try_cast<int>(), nullptr);
-    ASSERT_EQ(second.cast<int>(), 16);
+    ASSERT_NE(res.try_cast<int>(), nullptr);
+    ASSERT_EQ(res.cast<int>(), 50);
 
-    const auto third = type.invoke("f"_hs, instance, 5);
+    res = type.invoke("f"_hs, std::as_const(instance), 5);
 
-    ASSERT_TRUE(third);
+    ASSERT_TRUE(res);
     ASSERT_EQ(overloaded_func_t::value, 3);
-    ASSERT_NE(third.try_cast<int>(), nullptr);
-    ASSERT_EQ(third.cast<int>(), 25);
+    ASSERT_NE(res.try_cast<int>(), nullptr);
+    ASSERT_EQ(res.cast<int>(), 25);
 
-    const auto fourth = type.invoke("f"_hs, instance, 6, 7.f);
+    res = type.invoke("f"_hs, instance, 6, 7.f);
 
-    ASSERT_TRUE(fourth);
+    ASSERT_TRUE(res);
     ASSERT_EQ(overloaded_func_t::value, 6);
-    ASSERT_NE(fourth.try_cast<float>(), nullptr);
-    ASSERT_EQ(fourth.cast<float>(), 14.f);
+    ASSERT_NE(res.try_cast<float>(), nullptr);
+    ASSERT_EQ(res.cast<float>(), 14.f);
 
-    const auto cast = type.invoke("f"_hs, instance, 8, 9.f);
+    res = type.invoke("f"_hs, instance, 8, 9.f);
 
-    ASSERT_TRUE(cast);
+    ASSERT_TRUE(res);
     ASSERT_EQ(overloaded_func_t::value, 8);
-    ASSERT_NE(cast.try_cast<float>(), nullptr);
-    ASSERT_EQ(cast.cast<float>(), 18.f);
-
-    const auto ambiguous = type.invoke("f"_hs, instance, 8, 9.);
+    ASSERT_NE(res.try_cast<float>(), nullptr);
+    ASSERT_EQ(res.cast<float>(), 18.f);
 
-    ASSERT_FALSE(ambiguous);
+    // it fails as an ambiguous call
+    ASSERT_FALSE(type.invoke("f"_hs, instance, 8, 9.));
 }
 
 TEST_F(MetaType, Construct) {