meta: support for pointer-like types with a custom dereference operator other than operator*
This commit is contained in:
3
TODO
3
TODO
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
40
src/entt/meta/adl_pointer.hpp
Normal file
40
src/entt/meta/adl_pointer.hpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ENTT_META_POINTER_HPP
|
||||
#define ENTT_META_POINTER_HPP
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "type_traits.hpp"
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user