Forráskód Böngészése

meta:
* implicitly generated default constructor (overridable)
* pointer-like to void support (no dereference)

Michele Caini 5 éve
szülő
commit
9b80c0028d

+ 34 - 0
docs/md/meta.md

@@ -12,6 +12,7 @@
   * [Enjoy the runtime](#enjoy-the-runtime)
   * [Container support](#container-support)
   * [Pointer-like types](#pointer-like-types)
+  * [Implicitly generated default constructor](#implicitly-generated-default-constructor)
   * [Policies: the more, the less](#policies-the-more-the-less)
   * [Named constants and enums](#named-constants-and-enums)
   * [Properties and meta objects](#properties-and-meta-objects)
@@ -661,6 +662,39 @@ In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
 In all other cases, that is, when dereferencing a pointer works as expected and
 regardless of the pointed type, no user intervention is required.
 
+## Implicitly generated default constructor
+
+In many cases, it's useful to be able to create objects of default constructible
+types through the reflection system, while not having to explicitly register the
+meta type or the default constructor.<br/>
+For example, in the case of primitive types like `int` or `char`, but not just
+them.
+
+For this reason and only for default constructible types, a default constructor
+is automatically defined and associated with their meta types, whether they are
+explicitly or implicitly generated.<br/>
+Therefore, it won't be necessary to do this in order to construct an integer
+from its meta type:
+
+```cpp
+entt::meta<int>().ctor<>();
+```
+
+Instead, just do this:
+
+```cpp
+entt::resolve<int>().construct();
+```
+
+Where the meta type can be for example the one returned from a meta container,
+useful for building keys without knowing or having to register the actual types.
+
+In all cases, when users register custom defaul constructors, they are preferred
+both during searches and when the `construct` member function is invoked.<br/>
+However, the implicitly generated default constructor will always be returned,
+either if one is not explicitly specified or if all constructors are iterated
+for some reason (in this case, it will always be the last element).
+
 ## Policies: the more, the less
 
 Policies are a kind of compile-time directives that can be used when registering

+ 44 - 14
src/entt/meta/meta.hpp

@@ -4,13 +4,13 @@
 
 #include <array>
 #include <cstddef>
-#include <iterator>
 #include <functional>
+#include <iterator>
+#include <memory>
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
 #include "../core/any.hpp"
-#include "../core/attribute.h"
 #include "../core/fwd.hpp"
 #include "../core/utility.hpp"
 #include "../core/type_info.hpp"
@@ -127,9 +127,9 @@ struct meta_type_node {
     size_type(* const extent)(size_type);
     meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT;
     meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT;
+    meta_ctor_node *ctor{nullptr};
     meta_base_node *base{nullptr};
     meta_conv_node *conv{nullptr};
-    meta_ctor_node *ctor{nullptr};
     meta_data_node *data{nullptr};
     meta_func_node *func{nullptr};
     void(* dtor)(void *){nullptr};
@@ -157,6 +157,10 @@ auto meta_visit(const Op &op, const Node *node)
 }
 
 
+template<typename Type>
+meta_ctor_node * meta_default_constructor(meta_type_node *);
+
+
 template<typename Type>
 class ENTT_API meta_node {
     static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
@@ -169,7 +173,7 @@ class ENTT_API meta_node {
     }
 
 public:
-    [[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT {
+    [[nodiscard]] static internal::meta_type_node *  resolve() ENTT_NOEXCEPT {
         static meta_type_node node{
             type_id<Type>(),
             {},
@@ -191,11 +195,10 @@ public:
             has_meta_sequence_container_traits_v<Type>,
             has_meta_associative_container_traits_v<Type>,
             std::rank_v<Type>,
-            [](meta_type_node::size_type dim) {
-                return extent(dim, std::make_index_sequence<std::rank_v<Type>>{});
-            },
+            [](meta_type_node::size_type dim) { return extent(dim, std::make_index_sequence<std::rank_v<Type>>{}); },
             &meta_node<std::remove_cv_t<std::remove_pointer_t<Type>>>::resolve,
-            &meta_node<std::remove_cv_t<std::remove_extent_t<Type>>>::resolve
+            &meta_node<std::remove_cv_t<std::remove_extent_t<Type>>>::resolve,
+            meta_default_constructor<Type>(&node)
         };
 
         return &node;
@@ -354,8 +357,10 @@ class meta_any {
         case operation::DEREF:
         case operation::CDEREF:
             if constexpr(is_meta_pointer_like_v<Type>) {
-                auto &&obj = adl_meta_pointer_like<Type>::dereference(any_cast<const Type>(from));
-                *static_cast<meta_any *>(to) = op == operation::DEREF ? meta_any{std::reference_wrapper{obj}} : meta_any{std::cref(obj)};
+                if constexpr(!std::is_same_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>, void>) {
+                    auto &&obj = adl_meta_pointer_like<Type>::dereference(any_cast<const Type>(from));
+                    *static_cast<meta_any *>(to) = (op == operation::DEREF ? meta_any{std::reference_wrapper{obj}} : meta_any{std::cref(obj)});
+                }
             }
             break;
         case operation::SEQ:
@@ -515,7 +520,7 @@ public:
     template<typename Type>
     [[nodiscard]] const Type * try_cast() const {
         if(node) {
-            if(const auto info = internal::meta_info<Type>::resolve()->info; node->info == info) {
+            if(const auto info = type_id<Type>(); node->info == info) {
                 return any_cast<Type>(&storage);
             } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) {
                 return static_cast<const Type *>(base->cast(storage.data()));
@@ -529,7 +534,7 @@ public:
     template<typename Type>
     [[nodiscard]] Type * try_cast() {
         if(node) {
-            if(const auto info = internal::meta_info<Type>::resolve()->info; node->info == info) {
+            if(const auto info = type_id<Type>(); node->info == info) {
                 return any_cast<Type>(&storage);
             } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) {
                 return static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(base->cast(static_cast<constness_as_t<any, Type> &>(storage).data())));
@@ -577,7 +582,7 @@ public:
         if(try_cast<std::remove_reference_t<Type>>() != nullptr) {
             return as_ref(*this);
         } else if(node) {
-            if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = internal::meta_info<Type>::resolve()->info](const auto *curr) { return curr->type()->info == info; }, node); conv) {
+            if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id<Type>()](const auto *curr) { return curr->type()->info == info; }, node); conv) {
                 return conv->conv(storage.data());
             }
         }
@@ -595,7 +600,7 @@ public:
         if(try_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>() != nullptr) {
             return true;
         } else if(node) {
-            if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = internal::meta_info<Type>::resolve()->info](const auto *curr) { return curr->type()->info == info; }, node); conv) {
+            if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id<Type>()](const auto *curr) { return curr->type()->info == info; }, node); conv) {
                 *this = conv->conv(std::as_const(storage).data());
                 return true;
             }
@@ -2252,6 +2257,31 @@ inline bool meta_associative_container::erase(meta_any key) {
 }
 
 
+namespace internal {
+
+
+template<typename Type>
+meta_ctor_node * meta_default_constructor(meta_type_node *type) {
+    if constexpr(std::is_default_constructible_v<Type>) {
+        static internal::meta_ctor_node node{
+            type,
+            nullptr,
+            nullptr,
+            0u,
+            [](typename meta_ctor::size_type) ENTT_NOEXCEPT -> meta_type_node * { return nullptr; },
+            [](meta_any * const) { return meta_any{std::in_place_type<Type>}; }
+        };
+
+        return &node;
+    } else {
+        return nullptr;
+    }
+}
+
+
+}
+
+
 }
 
 

+ 54 - 1
test/entt/meta/meta_ctor.cpp

@@ -30,11 +30,13 @@ struct clazz_t {
     char c{};
 };
 
+double double_factory() { return 42.; }
+
 struct MetaCtor: ::testing::Test {
     static void SetUpTestCase() {
         using namespace entt::literals;
 
-        entt::meta<double>().conv<int>();
+        entt::meta<double>().conv<int>().ctor<&double_factory>();
         entt::meta<derived_t>().base<base_t>();
 
         entt::meta<clazz_t>().type("clazz"_hs)
@@ -207,3 +209,54 @@ TEST_F(MetaCtor, ExternalMemberFunction) {
     ASSERT_EQ(registry.get<clazz_t>(entity).i, 3);
     ASSERT_EQ(registry.get<clazz_t>(entity).c, 'c');
 }
+
+TEST_F(MetaCtor, ImplicitlyGeneratedDefaultConstructor) {
+    auto type = entt::resolve<int>();
+    int counter{};
+
+    for([[maybe_unused]] auto curr: type.ctor()) {
+        ++counter;
+    }
+
+    // default constructor is implicitly generated
+    ASSERT_EQ(counter, 1);
+    ASSERT_TRUE(type.ctor<>());
+
+    auto any = type.ctor<>().invoke();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.type(), entt::resolve<int>());
+    ASSERT_EQ(any.cast<int>(), 0);
+}
+
+TEST_F(MetaCtor, OverrideImplicitlyGeneratedDefaultConstructor) {
+    auto type = entt::resolve<double>();
+    int counter{};
+
+    for([[maybe_unused]] auto curr: type.ctor()) {
+        ++counter;
+    }
+
+    // default constructor is implicitly generated
+    ASSERT_EQ(counter, 2);
+    ASSERT_TRUE(type.ctor<>());
+
+    auto any = type.ctor<>().invoke();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.type(), entt::resolve<double>());
+    ASSERT_EQ(any.cast<double>(), 42.);
+}
+
+TEST_F(MetaCtor, NonDefaultConstructibleType) {
+    auto type = entt::resolve<clazz_t>();
+    int counter{};
+
+    for([[maybe_unused]] auto curr: type.ctor()) {
+        ++counter;
+    }
+
+    // the implicitly generated default constructor doesn't exist
+    ASSERT_EQ(counter, 5);
+    ASSERT_FALSE(type.ctor<>());
+}

+ 32 - 2
test/entt/meta/meta_pointer.cpp

@@ -202,7 +202,7 @@ TEST(MetaPointerLike, AsConstRef) {
     ASSERT_EQ(value, 42);
 }
 
-TEST(MetaPointerLike, DereferenceMetaPointerLikeOverload) {
+TEST(MetaPointerLike, DereferenceOverload) {
     auto test = [](entt::meta_any any) {
         ASSERT_FALSE(any.type().is_pointer());
         ASSERT_TRUE(any.type().is_pointer_like());
@@ -222,7 +222,7 @@ TEST(MetaPointerLike, DereferenceMetaPointerLikeOverload) {
     test(spec_wrapped_shared_ptr<int>{42});
 }
 
-TEST(MetaPointerLike, DereferenceMetaPointerToConstLikeOverload) {
+TEST(MetaPointerLike, DereferencePointerToConstOverload) {
     auto test = [](entt::meta_any any) {
         ASSERT_FALSE(any.type().is_pointer());
         ASSERT_TRUE(any.type().is_pointer_like());
@@ -241,3 +241,33 @@ TEST(MetaPointerLike, DereferenceMetaPointerToConstLikeOverload) {
     test(adl_wrapped_shared_ptr<const int>{42});
     test(spec_wrapped_shared_ptr<const int>{42});
 }
+
+TEST(MetaPointerLike, PointerToVoidSupport) {
+    auto test = [](entt::meta_any any) {
+        ASSERT_TRUE(any.type().is_pointer());
+        ASSERT_TRUE(any.type().is_pointer_like());
+        ASSERT_EQ(any.type().remove_pointer(), entt::resolve<void>());
+
+        auto deref = *any;
+
+        ASSERT_FALSE(deref);
+    };
+
+    test(static_cast<void *>(nullptr));
+    test(static_cast<const void *>(nullptr));
+}
+
+TEST(MetaPointerLike, SmartPointerToVoidSupport) {
+    auto test = [](entt::meta_any any) {
+        ASSERT_TRUE(any.type().is_class());
+        ASSERT_FALSE(any.type().is_pointer());
+        ASSERT_TRUE(any.type().is_pointer_like());
+
+        auto deref = *any;
+
+        ASSERT_FALSE(deref);
+    };
+
+    test(std::shared_ptr<void>{});
+    test(std::unique_ptr<void, void(*)(void *)>{nullptr, nullptr});
+}

+ 8 - 1
test/entt/meta/meta_type.cpp

@@ -125,7 +125,7 @@ struct MetaType: ::testing::Test {
         entt::meta<clazz_t>()
             .type("clazz"_hs)
                 .prop(property_t::value, 42)
-            .ctor().ctor<const base_t &, int>()
+            .ctor<const base_t &, int>()
             .data<&clazz_t::value>("value"_hs)
             .func<&clazz_t::member>("member"_hs)
             .func<&clazz_t::func>("func"_hs);
@@ -259,10 +259,17 @@ TEST_F(MetaType, Ctor) {
         ++counter;
     }
 
+    // we only register a constructor, the default one is implicitly generated for us
     ASSERT_EQ(counter, 2);
     ASSERT_TRUE((type.ctor<>()));
     ASSERT_TRUE((type.ctor<const base_t &, int>()));
     ASSERT_TRUE((type.ctor<const derived_t &, double>()));
+
+    // use the implicitly generated default constructor
+    auto any = type.construct();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.type(), entt::resolve<clazz_t>());
 }
 
 TEST_F(MetaType, Data) {