Browse Source

meta: static functions that require the parent type as first argument are treated as (eventually const) member functions

Michele Caini 4 years ago
parent
commit
80082f9d51
2 changed files with 93 additions and 8 deletions
  1. 35 7
      src/entt/meta/utility.hpp
  2. 58 1
      test/entt/meta/meta_func.cpp

+ 35 - 7
src/entt/meta/utility.hpp

@@ -79,14 +79,33 @@ struct meta_function_descriptor<Type, Ret Class::*> {
  * @brief Meta function descriptor.
  * @brief Meta function descriptor.
  * @tparam Type Reflected type to which the meta function is associated.
  * @tparam Type Reflected type to which the meta function is associated.
  * @tparam Ret Function return type.
  * @tparam Ret Function return type.
- * @tparam Args Function arguments.
+ * @tparam MaybeType First function argument.
+ * @tparam Args Other function arguments.
+ */
+template<typename Type, typename Ret, typename MaybeType, typename... Args>
+struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> {
+    /*! @brief Meta function return type. */
+    using return_type = Ret;
+    /*! @brief Meta function arguments. */
+    using args_type = std::conditional_t<std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<MaybeType>>>, type_list<Args...>, type_list<MaybeType, Args...>>;
+
+    /*! @brief True if the meta function is const, false otherwise. */
+    static constexpr auto is_const = std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<MaybeType>>> && std::is_const_v<std::remove_reference_t<MaybeType>>;
+    /*! @brief True if the meta function is static, false otherwise. */
+    static constexpr auto is_static = !std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<MaybeType>>>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
  */
  */
-template<typename Type, typename Ret, typename... Args>
-struct meta_function_descriptor<Type, Ret (*)(Args...)> {
+template<typename Type, typename Ret>
+struct meta_function_descriptor<Type, Ret (*)()> {
     /*! @brief Meta function return type. */
     /*! @brief Meta function return type. */
     using return_type = Ret;
     using return_type = Ret;
     /*! @brief Meta function arguments. */
     /*! @brief Meta function arguments. */
-    using args_type = type_list<Args...>;
+    using args_type = type_list<>;
 
 
     /*! @brief True if the meta function is const, false otherwise. */
     /*! @brief True if the meta function is const, false otherwise. */
     static constexpr auto is_const = false;
     static constexpr auto is_const = false;
@@ -177,7 +196,8 @@ template<typename Type, auto Data>
 [[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
 [[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
     if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
     if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
         if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
         if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
-            using data_type = type_list_element_t<1u, typename meta_function_helper_t<Type, decltype(Data)>::args_type>;
+            using descriptor = typename meta_function_helper_t<Type, decltype(Data)>;
+            using data_type = type_list_element_t<descriptor::is_static, descriptor::args_type>;
 
 
             if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
             if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
                 Data(*clazz, value.cast<data_type>());
                 Data(*clazz, value.cast<data_type>());
@@ -368,7 +388,11 @@ template<typename Type, typename... Args>
  */
  */
 template<typename Type, typename Policy = as_is_t, typename Candidate>
 template<typename Type, typename Policy = as_is_t, typename Candidate>
 [[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) {
 [[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) {
-    return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+    if constexpr(typename meta_function_helper_t<Type, Candidate>::is_static) {
+        return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+    } else {
+        return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+    }
 }
 }
 
 
 /**
 /**
@@ -381,7 +405,11 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
  */
  */
 template<typename Type, auto Candidate, typename Policy = as_is_t>
 template<typename Type, auto Candidate, typename Policy = as_is_t>
 [[nodiscard]] meta_any meta_construct(meta_any *const args) {
 [[nodiscard]] meta_any meta_construct(meta_any *const args) {
-    return internal::meta_invoke<Type, Policy>({}, Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
+    if constexpr(typename meta_function_helper_t<Type, decltype(Candidate)>::is_static) {
+        return internal::meta_invoke<Type, Policy>({}, Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
+    } else {
+        return internal::meta_invoke<Type, Policy>(*args, Candidate, args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
+    }
 }
 }
 
 
 } // namespace entt
 } // namespace entt

+ 58 - 1
test/entt/meta/meta_func.cpp

@@ -21,6 +21,14 @@ struct base_t {
     int value{3};
     int value{3};
 };
 };
 
 
+void fake_member(base_t &instance, int value) {
+    instance.value = value;
+}
+
+int fake_const_member(const base_t &instance) {
+    return instance.value;
+}
+
 struct derived_t: base_t {
 struct derived_t: base_t {
     derived_t()
     derived_t()
         : base_t{} {}
         : base_t{} {}
@@ -77,7 +85,9 @@ struct MetaFunc: ::testing::Test {
         entt::meta<base_t>()
         entt::meta<base_t>()
             .type("base"_hs)
             .type("base"_hs)
             .dtor<base_t::destroy>()
             .dtor<base_t::destroy>()
-            .func<&base_t::func>("func"_hs);
+            .func<&base_t::func>("func"_hs)
+            .func<fake_member>("fake_member"_hs)
+            .func<fake_const_member>("fake_const_member"_hs);
 
 
         entt::meta<derived_t>()
         entt::meta<derived_t>()
             .type("derived"_hs)
             .type("derived"_hs)
@@ -299,6 +309,53 @@ TEST_F(MetaFunc, StaticRetVoid) {
     ASSERT_FALSE(prop.value().cast<bool>());
     ASSERT_FALSE(prop.value().cast<bool>());
 }
 }
 
 
+TEST_F(MetaFunc, StaticAsMember) {
+    using namespace entt::literals;
+
+    base_t instance{};
+    auto func = entt::resolve<base_t>().func("fake_member"_hs);
+    auto any = func.invoke(instance, 42);
+
+    ASSERT_TRUE(func);
+    ASSERT_EQ(func.id(), "fake_member"_hs);
+    ASSERT_EQ(func.arity(), 1u);
+    ASSERT_FALSE(func.is_const());
+    ASSERT_FALSE(func.is_static());
+    ASSERT_EQ(func.ret(), entt::resolve<void>());
+    ASSERT_EQ(func.arg(0u), entt::resolve<int>());
+    ASSERT_FALSE(func.arg(1u));
+
+    ASSERT_FALSE(func.invoke({}, 42));
+    ASSERT_FALSE(func.invoke(std::as_const(instance), 42));
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.type(), entt::resolve<void>());
+    ASSERT_EQ(instance.value, 42);
+}
+
+TEST_F(MetaFunc, StaticAsConstMember) {
+    using namespace entt::literals;
+
+    base_t instance{};
+    auto func = entt::resolve<base_t>().func("fake_const_member"_hs);
+    auto any = func.invoke(std::as_const(instance));
+
+    ASSERT_TRUE(func);
+    ASSERT_EQ(func.id(), "fake_const_member"_hs);
+    ASSERT_EQ(func.arity(), 0u);
+    ASSERT_TRUE(func.is_const());
+    ASSERT_FALSE(func.is_static());
+    ASSERT_EQ(func.ret(), entt::resolve<int>());
+    ASSERT_FALSE(func.arg(0u));
+
+    ASSERT_FALSE(func.invoke({}));
+    ASSERT_TRUE(func.invoke(instance));
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.type(), entt::resolve<int>());
+    ASSERT_EQ(any.cast<int>(), 3);
+}
+
 TEST_F(MetaFunc, MetaAnyArgs) {
 TEST_F(MetaFunc, MetaAnyArgs) {
     using namespace entt::literals;
     using namespace entt::literals;