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

This commit is contained in:
Michele Caini
2020-12-23 17:31:49 +01:00
parent ac8bde4ea8
commit 2abcbcd30f
7 changed files with 156 additions and 11 deletions

3
TODO
View File

@@ -19,7 +19,6 @@
WIP:
* 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: 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
@@ -27,9 +26,7 @@ WIP:
* 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: 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
* suppress warnings in meta.hpp (uninitialized members)
* add exclude-only views to combine with packs
* 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 File

@@ -611,7 +611,7 @@ the `is_meta_pointer_like` class. `EnTT` already exports the specializations for
some common classes. In particular:
* 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
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`
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 are a kind of compile-time directives that can be used when registering

View File

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

View File

@@ -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

View File

@@ -14,6 +14,7 @@
#include "../core/fwd.hpp"
#include "../core/utility.hpp"
#include "../core/type_info.hpp"
#include "adl_pointer.hpp"
#include "ctx.hpp"
#include "internal.hpp"
#include "range.hpp"
@@ -164,12 +165,12 @@ class meta_any {
switch(op) {
case operation::DEREF:
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;
case operation::CDEREF:
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;
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 nullptr;
}

View File

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

View File

@@ -1,9 +1,45 @@
#include <memory>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/pointer.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 {
not_copyable_t() = default;
not_copyable_t(const not_copyable_t &) = delete;
@@ -165,3 +201,43 @@ TEST(MetaPointerLike, AsConstRef) {
ASSERT_EQ(*any.cast<int *>(), 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});
}