Explorar o código

meta_func/meta_ctor: support for registering external member functions

Michele Caini %!s(int64=5) %!d(string=hai) anos
pai
achega
6ad884080c
Modificáronse 4 ficheiros con 82 adicións e 29 borrados
  1. 0 1
      TODO
  2. 30 28
      src/entt/meta/factory.hpp
  3. 26 0
      test/entt/meta/meta_ctor.cpp
  4. 26 0
      test/entt/meta/meta_func.cpp

+ 0 - 1
TODO

@@ -13,7 +13,6 @@ WIP:
 * view pack: doc and plain function as an alias for operator|
 * reverse iterators, rbegin and rend for the view pack
 * use extended get to remove is_eto_eligible checks from views and groups
-* factory invoke: add support for external member functions as static meta functions
 * pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
 * make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use
 * add observer functions aside observer class

+ 30 - 28
src/entt/meta/factory.hpp

@@ -29,15 +29,16 @@ namespace entt {
 namespace internal {
 
 
-template<typename, bool = false>
+template<typename, bool, bool>
 struct meta_function_helper;
 
 
-template<typename Ret, typename... Args, bool Const>
-struct meta_function_helper<Ret(Args...), Const> {
+template<typename Ret, typename... Args, bool Const, bool Static>
+struct meta_function_helper<Ret(Args...), Const, Static> {
     using return_type = std::remove_cv_t<std::remove_reference_t<Ret>>;
     using args_type = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
 
+    static constexpr auto is_static = Static;
     static constexpr auto is_const = Const;
 
     [[nodiscard]] static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT {
@@ -46,26 +47,27 @@ struct meta_function_helper<Ret(Args...), Const> {
 };
 
 
-template<typename Ret, typename... Args, typename Class>
-constexpr meta_function_helper<Ret(Args...), true>
+template<typename Type, typename Ret, typename... Args, typename Class>
+constexpr meta_function_helper<std::conditional_t<std::is_same_v<Type, Class>, Ret(Args...), Ret(std::add_lvalue_reference_t<Class>, Args...)>, true, !std::is_same_v<Type, Class>>
 to_meta_function_helper(Ret(Class:: *)(Args...) const);
 
 
-template<typename Ret, typename... Args, typename Class>
-constexpr meta_function_helper<Ret(Args...)>
+template<typename Type, typename Ret, typename... Args, typename Class>
+constexpr meta_function_helper<std::conditional_t<std::is_same_v<Type, Class>, Ret(Args...), Ret(std::add_lvalue_reference_t<Class>, Args...)>, false, !std::is_same_v<Type, Class>>
 to_meta_function_helper(Ret(Class:: *)(Args...));
 
 
-template<typename Ret, typename... Args>
-constexpr meta_function_helper<Ret(Args...)>
+template<typename Type, typename Ret, typename... Args>
+constexpr meta_function_helper<Ret(Args...), false, true>
 to_meta_function_helper(Ret(*)(Args...));
 
 
+template<typename Type>
 constexpr void to_meta_function_helper(...);
 
 
-template<typename Candidate>
-using meta_function_helper_t = decltype(to_meta_function_helper(std::declval<Candidate>()));
+template<typename Type, typename Candidate>
+using meta_function_helper_t = decltype(to_meta_function_helper<Type>(std::declval<Candidate>()));
 
 
 template<typename Type, typename... Args, std::size_t... Indexes>
@@ -82,7 +84,7 @@ template<typename Type, auto Data>
     bool accepted = false;
 
     if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
-        using helper_type = meta_function_helper_t<decltype(Data)>;
+        using helper_type = meta_function_helper_t<Type, decltype(Data)>;
         using data_type = std::tuple_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>;
 
         if(auto * const clazz = instance->try_cast<Type>(); clazz) {
@@ -154,7 +156,7 @@ template<typename Type, auto Data, typename Policy>
 
 template<typename Type, auto Candidate, typename Policy, std::size_t... Indexes>
 [[nodiscard]] meta_any invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence<Indexes...>) {
-    using helper_type = meta_function_helper_t<decltype(Candidate)>;
+    using helper_type = meta_function_helper_t<Type, decltype(Candidate)>;
 
     auto dispatch = [](auto *... params) {
         if constexpr(std::is_void_v<typename helper_type::return_type> || std::is_same_v<Policy, as_void_t>) {
@@ -178,11 +180,11 @@ template<typename Type, auto Candidate, typename Policy, std::size_t... Indexes>
         return value;
     }(args+Indexes, (args+Indexes)->try_cast<std::tuple_element_t<Indexes, typename helper_type::args_type>>())...);
 
-    if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Candidate)>>>) {
-        return (std::get<Indexes>(direct) && ...) ? dispatch(std::get<Indexes>(direct)...) : meta_any{};
-    } else {
+    if constexpr(std::is_invocable_v<decltype(Candidate), std::add_lvalue_reference_t<Type>, std::tuple_element_t<Indexes, typename helper_type::args_type>...>) {
         auto * const clazz = instance->try_cast<Type>();
         return (clazz && (std::get<Indexes>(direct) && ...)) ? dispatch(clazz, std::get<Indexes>(direct)...) : meta_any{};
+    } else {
+        return (std::get<Indexes>(direct) && ...) ? dispatch(std::get<Indexes>(direct)...) : meta_any{};
     }
 }
 
@@ -454,19 +456,19 @@ public:
     /**
      * @brief Assigns a meta constructor to a meta type.
      *
-     * Free functions can be assigned to meta types in the role of constructors.
-     * All that is required is that they return an instance of the underlying
-     * type.<br/>
+     * Both member functions and free function can be assigned to meta types in
+     * the role of constructors. All that is required is that they return an
+     * instance of the underlying type.<br/>
      * From a client's point of view, nothing changes if a constructor of a meta
-     * type is a built-in one or a free function.
+     * type is a built-in one or not.
      *
-     * @tparam Func The actual function to use as a constructor.
+     * @tparam Candidate The actual function to use as a constructor.
      * @tparam Policy Optional policy (no policy set by default).
      * @return An extended meta factory for the parent type.
      */
-    template<auto Func, typename Policy = as_is_t>
+    template<auto Candidate, typename Policy = as_is_t>
     auto ctor() ENTT_NOEXCEPT {
-        using helper_type = internal::meta_function_helper_t<decltype(Func)>;
+        using helper_type = internal::meta_function_helper_t<Type, decltype(Candidate)>;
         static_assert(std::is_same_v<typename helper_type::return_type, Type>, "The function doesn't return an object of the required type");
         auto * const type = internal::meta_info<Type>::resolve();
 
@@ -477,7 +479,7 @@ public:
             std::tuple_size_v<typename helper_type::args_type>,
             &helper_type::arg,
             [](meta_any * const any) {
-                return internal::invoke<Type, Func, Policy>({}, any, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
+                return internal::invoke<Type, Candidate, Policy>({}, any, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
             }
         };
 
@@ -485,7 +487,7 @@ public:
         node.next = type->ctor;
         type->ctor = &node;
 
-        return meta_factory<Type, std::integral_constant<decltype(Func), Func>>{&node.prop};
+        return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
     }
 
     /**
@@ -500,7 +502,7 @@ public:
      */
     template<typename... Args>
     auto ctor() ENTT_NOEXCEPT {
-        using helper_type = internal::meta_function_helper_t<Type(*)(Args...)>;
+        using helper_type = internal::meta_function_helper_t<Type, Type(*)(Args...)>;
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_ctor_node node{
@@ -667,7 +669,7 @@ public:
      */
     template<auto Candidate, typename Policy = as_is_t>
     auto func(const id_type id) ENTT_NOEXCEPT {
-        using helper_type = internal::meta_function_helper_t<decltype(Candidate)>;
+        using helper_type = internal::meta_function_helper_t<Type, decltype(Candidate)>;
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_func_node node{
@@ -677,7 +679,7 @@ public:
             nullptr,
             std::tuple_size_v<typename helper_type::args_type>,
             helper_type::is_const,
-            !std::is_member_function_pointer_v<decltype(Candidate)>,
+            helper_type::is_static,
             &internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename helper_type::return_type>>::resolve,
             &helper_type::arg,
             [](meta_handle instance, meta_any *args) {

+ 26 - 0
test/entt/meta/meta_ctor.cpp

@@ -1,6 +1,7 @@
 #include <gtest/gtest.h>
 #include <entt/core/hashed_string.hpp>
 #include <entt/core/utility.hpp>
+#include <entt/entity/registry.hpp>
 #include <entt/meta/factory.hpp>
 #include <entt/meta/meta.hpp>
 #include <entt/meta/resolve.hpp>
@@ -35,6 +36,7 @@ struct MetaCtor: ::testing::Test {
         entt::meta<derived_t>().base<base_t>();
 
         entt::meta<clazz_t>().type("clazz"_hs)
+            .ctor<&entt::registry::emplace_or_replace<clazz_t, const int &, const char &>, entt::as_ref_t>()
             .ctor<const base_t &, int>()
             .ctor<const int &, char>().prop(3, false)
             .ctor<entt::overload<clazz_t(int)>(&clazz_t::factory)>().prop('c', 42)
@@ -148,3 +150,27 @@ TEST_F(MetaCtor, FuncCastAndConvert) {
     ASSERT_EQ(any.cast<clazz_t>().i, 9);
     ASSERT_EQ(any.cast<clazz_t>().c, 'c');
 }
+
+TEST_F(MetaCtor, ExternalMemberFunction) {
+    auto ctor = entt::resolve<clazz_t>().ctor<entt::registry &, entt::entity, const int &, const char &>();
+
+    ASSERT_TRUE(ctor);
+    ASSERT_EQ(ctor.parent(), entt::resolve("clazz"_hs));
+    ASSERT_EQ(ctor.size(), 4u);
+    ASSERT_EQ(ctor.arg(0u), entt::resolve<entt::registry>());
+    ASSERT_EQ(ctor.arg(1u), entt::resolve<entt::entity>());
+    ASSERT_EQ(ctor.arg(2u), entt::resolve<int>());
+    ASSERT_EQ(ctor.arg(3u), entt::resolve<char>());
+    ASSERT_FALSE(ctor.arg(4u));
+
+    entt::registry registry;
+    const auto entity = registry.create();
+
+    ASSERT_FALSE(registry.has<clazz_t>(entity));
+
+    ctor.invoke(std::ref(registry), entity, 3, 'c');
+
+    ASSERT_TRUE(registry.has<clazz_t>(entity));
+    ASSERT_EQ(registry.get<clazz_t>(entity).i, 3);
+    ASSERT_EQ(registry.get<clazz_t>(entity).c, 'c');
+}

+ 26 - 0
test/entt/meta/meta_func.cpp

@@ -1,6 +1,7 @@
 #include <gtest/gtest.h>
 #include <entt/core/hashed_string.hpp>
 #include <entt/core/utility.hpp>
+#include <entt/entity/registry.hpp>
 #include <entt/meta/factory.hpp>
 #include <entt/meta/meta.hpp>
 #include <entt/meta/resolve.hpp>
@@ -65,6 +66,7 @@ struct MetaFunc: ::testing::Test {
         entt::meta<derived_t>().base<base_t>().dtor<&derived_t::destroy>();
 
         entt::meta<func_t>().type("func"_hs)
+            .func<&entt::registry::emplace_or_replace<func_t>, entt::as_ref_t>("emplace"_hs)
             .func<entt::overload<int(const base_t &, int, int)>(&func_t::f)>("f3"_hs)
             .func<entt::overload<int(int, int)>(&func_t::f)>("f2"_hs).prop(true, false)
             .func<entt::overload<int(int) const>(&func_t::f)>("f1"_hs).prop(true, false)
@@ -328,3 +330,27 @@ TEST_F(MetaFunc, FromBase) {
 
     ASSERT_EQ(instance.value, 42);
 }
+
+TEST_F(MetaFunc, ExternalMemberFunction) {
+    auto func = entt::resolve<func_t>().func("emplace"_hs);
+
+    ASSERT_TRUE(func);
+    ASSERT_EQ(func.parent(), entt::resolve("func"_hs));
+    ASSERT_EQ(func.id(), "emplace"_hs);
+    ASSERT_EQ(func.size(), 2u);
+    ASSERT_FALSE(func.is_const());
+    ASSERT_TRUE(func.is_static());
+    ASSERT_EQ(func.ret(), entt::resolve<void>());
+    ASSERT_EQ(func.arg(0u), entt::resolve<entt::registry>());
+    ASSERT_EQ(func.arg(1u), entt::resolve<entt::entity>());
+    ASSERT_FALSE(func.arg(2u));
+
+    entt::registry registry;
+    const auto entity = registry.create();
+    
+    ASSERT_FALSE(registry.has<func_t>(entity));
+    
+    func.invoke({}, std::ref(registry), entity);
+    
+    ASSERT_TRUE(registry.has<func_t>(entity));
+}