Bladeren bron

meta: support for pointer-like types with a custom dereference operator other than operator*

Michele Caini 5 jaren geleden
bovenliggende
commit
2abcbcd30f
7 gewijzigde bestanden met toevoegingen van 156 en 11 verwijderingen
  1. 0 3
      TODO
  2. 34 5
      docs/md/meta.md
  3. 1 0
      src/entt/entt.hpp
  4. 40 0
      src/entt/meta/adl_pointer.hpp
  5. 4 3
      src/entt/meta/meta.hpp
  6. 1 0
      src/entt/meta/pointer.hpp
  7. 76 0
      test/entt/meta/meta_pointer.cpp

+ 0 - 3
TODO

@@ -19,7 +19,6 @@
 
 
 WIP:
 WIP:
 * HP: const poly storage function for the registry
 * HP: const poly storage function for the registry
-* HP: meta, support for custom deref function other than operator*
 * HP: headless (sparse set only) view
 * HP: headless (sparse set only) view
 * HP: pass the registry to pools, basic poly storage should have only component member
 * HP: pass the registry to pools, basic poly storage should have only component member
 * HP: make view pack work also with groups, make packs input iterator only, add view adapter for external sources
 * HP: make view pack work also with groups, make packs input iterator only, add view adapter for external sources
@@ -27,9 +26,7 @@ WIP:
 * HP: any/poly: configurable sbo size, compile-time policies like sbo-required.
 * HP: any/poly: configurable sbo size, compile-time policies like sbo-required.
 * HP: registry: use a poly object for pools, no more pool_data type.
 * HP: registry: use a poly object for pools, no more pool_data type.
 * HP: make runtime views use opaque storage and therefore return also elements.
 * HP: make runtime views use opaque storage and therefore return also elements.
-* HP: meta_sequence_container & Co can be replaced by a poly object.
 * HP: poly: support for data members
 * HP: poly: support for data members
-* suppress warnings in meta.hpp (uninitialized members)
 * add exclude-only views to combine with packs
 * add exclude-only views to combine with packs
 * deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
 * deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
 * view pack: plain function as an alias for operator|, reverse iterators, rbegin and rend
 * view pack: plain function as an alias for operator|, reverse iterators, rbegin and rend

+ 34 - 5
docs/md/meta.md

@@ -611,7 +611,7 @@ the `is_meta_pointer_like` class. `EnTT` already exports the specializations for
 some common classes. In particular:
 some common classes. In particular:
 
 
 * All types of raw pointers.
 * All types of raw pointers.
-* `std::uniqe_ptr` and `std::shared_ptr`.
+* `std::unique_ptr` and `std::shared_ptr`.
 
 
 It's important to include the header file `pointer.hpp` to make these
 It's important to include the header file `pointer.hpp` to make these
 specializations available to the compiler when needed.<br/>
 specializations available to the compiler when needed.<br/>
@@ -635,13 +635,42 @@ if(any.type().is_pointer_like()) {
 }
 }
 ```
 ```
 
 
-It goes without saying that it's not necessary to perform a double check.
-Instead, it's sufficient to query the meta type or verify that the returned
-object is valid. For example, invalid instances are returned when the wrapped
-object hasn't a pointer-like type.<br/>
+Of course, it's not necessary to perform a double check. Instead, it's enough to
+query the meta type or verify that the returned object is valid. For example,
+invalid instances are returned when the wrapped object isn't a pointer-like
+type.<br/>
 Note that dereferencing a pointer-like object returns an instance of `meta_any`
 Note that dereferencing a pointer-like object returns an instance of `meta_any`
 which refers to the pointed object and allows users to modify it directly.
 which refers to the pointed object and allows users to modify it directly.
 
 
+In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
+`EnTT` also supports classes that don't offer an `operator*`. In particular:
+
+* It's possible to exploit a solution based on ADL lookup by offering a function
+  (also a template one) named `dereference_meta_pointer_like`:
+
+  ```cpp
+  template<typename Type>
+  Type & dereference_meta_pointer_like(const custom_pointer_type<Type> &ptr) {
+      return ptr.deref();
+  }
+  ```
+
+* When not in control of the type's namespace, it's possible to inject into the
+  `entt` namespace a specialization of `adl_meta_pointer_like` class template to
+  bypass the adl lookup as a whole:
+
+  ```cpp
+  template<typename Type>
+  struct entt::adl_meta_pointer_like<custom_pointer_type<Type>> {
+      static decltype(auto) dereference(const custom_pointer_type<Type> &ptr) {
+          return ptr.deref();
+      }
+  };
+  ```
+
+In all other cases, that is, when dereferencing a pointer works as expected and
+regardless of the pointed type, no user intervention is required.
+
 ## Policies: the more, the less
 ## Policies: the more, the less
 
 
 Policies are a kind of compile-time directives that can be used when registering
 Policies are a kind of compile-time directives that can be used when registering

+ 1 - 0
src/entt/entt.hpp

@@ -25,6 +25,7 @@
 #include "entity/view.hpp"
 #include "entity/view.hpp"
 #include "entity/view_pack.hpp"
 #include "entity/view_pack.hpp"
 #include "locator/locator.hpp"
 #include "locator/locator.hpp"
+#include "meta/adl_pointer.hpp"
 #include "meta/container.hpp"
 #include "meta/container.hpp"
 #include "meta/ctx.hpp"
 #include "meta/ctx.hpp"
 #include "meta/factory.hpp"
 #include "meta/factory.hpp"

+ 40 - 0
src/entt/meta/adl_pointer.hpp

@@ -0,0 +1,40 @@
+#ifndef ENTT_META_ADL_POINTER_HPP
+#define ENTT_META_ADL_POINTER_HPP
+
+
+namespace entt {
+
+
+/**
+ * @brief ADL based lookup function for dereferencing meta pointer-like types.
+ * @tparam Type Element type.
+ * @param value A pointer-like object.
+ * @return The value returned from the dereferenced pointer.
+ */
+template<typename Type>
+decltype(auto) dereference_meta_pointer_like(const Type &value) {
+    return *value;
+}
+
+
+/**
+ * @brief Fake ADL based lookup function for meta pointer-like types.
+ * @tparam Type Element type.
+ */
+template<typename Type>
+struct adl_meta_pointer_like {
+    /**
+     * @brief Uses the default ADL based lookup method to resolve the call.
+     * @param value A pointer-like object.
+     * @return The value returned from the dereferenced pointer.
+     */
+    static decltype(auto) dereference(const Type &value) {
+        return dereference_meta_pointer_like(value);
+    }
+};
+
+
+}
+
+
+#endif

+ 4 - 3
src/entt/meta/meta.hpp

@@ -14,6 +14,7 @@
 #include "../core/fwd.hpp"
 #include "../core/fwd.hpp"
 #include "../core/utility.hpp"
 #include "../core/utility.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_info.hpp"
+#include "adl_pointer.hpp"
 #include "ctx.hpp"
 #include "ctx.hpp"
 #include "internal.hpp"
 #include "internal.hpp"
 #include "range.hpp"
 #include "range.hpp"
@@ -164,12 +165,12 @@ class meta_any {
         switch(op) {
         switch(op) {
         case operation::DEREF:
         case operation::DEREF:
             if constexpr(is_meta_pointer_like_v<Type>) {
             if constexpr(is_meta_pointer_like_v<Type>) {
-                *static_cast<meta_any *>(to) = std::reference_wrapper{*any_cast<const Type>(from)};
+                *static_cast<meta_any *>(to) = std::reference_wrapper{adl_meta_pointer_like<Type>::dereference(any_cast<const Type>(from))};
             }
             }
             break;
             break;
         case operation::CDEREF:
         case operation::CDEREF:
             if constexpr(is_meta_pointer_like_v<Type>) {
             if constexpr(is_meta_pointer_like_v<Type>) {
-                *static_cast<meta_any *>(to) = std::cref(*any_cast<const Type>(from));
+                *static_cast<meta_any *>(to) = std::cref(adl_meta_pointer_like<Type>::dereference(any_cast<const Type>(from)));
             }
             }
             break;
             break;
         case operation::SEQ:
         case operation::SEQ:
@@ -357,7 +358,7 @@ public:
                 return static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(base->cast(static_cast<constness_as_t<any, Type> &>(storage).data())));
                 return static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(base->cast(static_cast<constness_as_t<any, Type> &>(storage).data())));
             }
             }
         }
         }
-        
+
         return nullptr;
         return nullptr;
     }
     }
 
 

+ 1 - 0
src/entt/meta/pointer.hpp

@@ -1,6 +1,7 @@
 #ifndef ENTT_META_POINTER_HPP
 #ifndef ENTT_META_POINTER_HPP
 #define ENTT_META_POINTER_HPP
 #define ENTT_META_POINTER_HPP
 
 
+
 #include <memory>
 #include <memory>
 #include <type_traits>
 #include <type_traits>
 #include "type_traits.hpp"
 #include "type_traits.hpp"

+ 76 - 0
test/entt/meta/meta_pointer.cpp

@@ -1,9 +1,45 @@
+#include <memory>
+#include <type_traits>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <entt/core/hashed_string.hpp>
 #include <entt/core/hashed_string.hpp>
 #include <entt/meta/meta.hpp>
 #include <entt/meta/meta.hpp>
 #include <entt/meta/pointer.hpp>
 #include <entt/meta/pointer.hpp>
 #include <entt/meta/resolve.hpp>
 #include <entt/meta/resolve.hpp>
 
 
+template<typename Type>
+struct wrapped_shared_ptr {
+    wrapped_shared_ptr(Type init): ptr{new Type {init}} {}
+    
+    Type & deref() const { return *ptr; }
+
+private:
+    std::shared_ptr<Type> ptr;
+};
+
+template<typename Type>
+struct adl_wrapped_shared_ptr: wrapped_shared_ptr<Type> {};
+
+template<typename Type>
+struct spec_wrapped_shared_ptr: wrapped_shared_ptr<Type> {};
+
+template<typename Type>
+struct entt::is_meta_pointer_like<adl_wrapped_shared_ptr<Type>>: std::true_type {};
+
+template<typename Type>
+struct entt::is_meta_pointer_like<spec_wrapped_shared_ptr<Type>>: std::true_type {};
+
+template<typename Type>
+Type & dereference_meta_pointer_like(const adl_wrapped_shared_ptr<Type> &ptr) {
+    return ptr.deref();
+}
+
+template<typename Type>
+struct entt::adl_meta_pointer_like<spec_wrapped_shared_ptr<Type>> {
+    static decltype(auto) dereference(const spec_wrapped_shared_ptr<Type> &ptr) {
+        return ptr.deref();
+    }
+};
+
 struct not_copyable_t {
 struct not_copyable_t {
     not_copyable_t() = default;
     not_copyable_t() = default;
     not_copyable_t(const not_copyable_t &) = delete;
     not_copyable_t(const not_copyable_t &) = delete;
@@ -165,3 +201,43 @@ TEST(MetaPointerLike, AsConstRef) {
     ASSERT_EQ(*any.cast<int *>(), 42);
     ASSERT_EQ(*any.cast<int *>(), 42);
     ASSERT_EQ(value, 42);
     ASSERT_EQ(value, 42);
 }
 }
+
+TEST(MetaPointerLike, DereferenceMetaPointerLikeOverload) {
+    auto test = [](entt::meta_any any) {
+        ASSERT_FALSE(any.type().is_pointer());
+        ASSERT_TRUE(any.type().is_pointer_like());
+
+        auto deref = *any;
+
+        ASSERT_TRUE(deref);
+        ASSERT_FALSE(deref.type().is_pointer());
+        ASSERT_FALSE(deref.type().is_pointer_like());
+        ASSERT_EQ(deref.type(), entt::resolve<int>());
+
+        ASSERT_EQ(deref.cast<int &>(), 42);
+        ASSERT_EQ(deref.cast<const int &>(), 42);
+    };
+
+    test(adl_wrapped_shared_ptr<int>{42});
+    test(spec_wrapped_shared_ptr<int>{42});
+}
+
+TEST(MetaPointerLike, DereferenceMetaPointerToConstLikeOverload) {
+    auto test = [](entt::meta_any any) {
+        ASSERT_FALSE(any.type().is_pointer());
+        ASSERT_TRUE(any.type().is_pointer_like());
+
+        auto deref = *any;
+
+        ASSERT_TRUE(deref);
+        ASSERT_FALSE(deref.type().is_pointer());
+        ASSERT_FALSE(deref.type().is_pointer_like());
+        ASSERT_EQ(deref.type(), entt::resolve<int>());
+
+        ASSERT_DEATH(deref.cast<int &>() = 42, ".*");
+        ASSERT_EQ(deref.cast<const int &>(), 42);
+    };
+
+    test(adl_wrapped_shared_ptr<const int>{42});
+    test(spec_wrapped_shared_ptr<const int>{42});
+}