#include #include #include #include #include #include #include #include "../common/config.h" template struct wrapped_shared_ptr { wrapped_shared_ptr(Type init) : ptr{new Type{init}} {} Type &deref() const { return *ptr; } private: std::shared_ptr ptr; }; struct self_ptr { using element_type = self_ptr; self_ptr(int v) : value{v} {} const self_ptr &operator*() const { return *this; } int value; }; struct proxy_ptr { using element_type = proxy_ptr; proxy_ptr(int &v) : value{&v} {} proxy_ptr operator*() const { return *this; } int *value; }; template struct adl_wrapped_shared_ptr: wrapped_shared_ptr {}; template struct spec_wrapped_shared_ptr: wrapped_shared_ptr {}; template struct entt::is_meta_pointer_like>: std::true_type {}; template struct entt::is_meta_pointer_like>: std::true_type {}; template<> struct entt::is_meta_pointer_like: std::true_type {}; template<> struct entt::is_meta_pointer_like: std::true_type {}; template struct entt::adl_meta_pointer_like> { static decltype(auto) dereference(const spec_wrapped_shared_ptr &ptr) { return ptr.deref(); } }; template Type &dereference_meta_pointer_like(const adl_wrapped_shared_ptr &ptr) { return ptr.deref(); } int test_function() { return 42; } struct not_copyable_t { not_copyable_t() = default; not_copyable_t(const not_copyable_t &) = delete; not_copyable_t(not_copyable_t &&) = default; not_copyable_t &operator=(const not_copyable_t &) = delete; not_copyable_t &operator=(not_copyable_t &&) = default; }; TEST(MetaPointerLike, DereferenceOperatorInvalidType) { int value = 0; entt::meta_any any{value}; ASSERT_FALSE(any.type().is_pointer()); ASSERT_FALSE(any.type().is_pointer_like()); ASSERT_EQ(any.type(), entt::resolve()); auto deref = *any; ASSERT_FALSE(deref); } TEST(MetaPointerLike, DereferenceOperatorConstType) { const int value = 42; entt::meta_any any{&value}; ASSERT_TRUE(any.type().is_pointer()); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_EQ(any.type(), entt::resolve()); 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()); ASSERT_EQ(deref.try_cast(), nullptr); ASSERT_EQ(deref.try_cast(), &value); ASSERT_EQ(deref.cast(), 42); } ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstType) { const int value = 42; entt::meta_any any{&value}; auto deref = *any; ASSERT_TRUE(deref); ASSERT_DEATH(deref.cast() = 0, ""); } TEST(MetaPointerLike, DereferenceOperatorConstAnyNonConstType) { int value = 42; const entt::meta_any any{&value}; 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()); ASSERT_NE(deref.try_cast(), nullptr); ASSERT_NE(deref.try_cast(), nullptr); ASSERT_EQ(deref.cast(), 42); ASSERT_EQ(deref.cast(), 42); } TEST(MetaPointerLike, DereferenceOperatorConstAnyConstType) { const int value = 42; const entt::meta_any any{&value}; 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()); ASSERT_EQ(deref.try_cast(), nullptr); ASSERT_NE(deref.try_cast(), nullptr); ASSERT_EQ(deref.cast(), 42); } ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstAnyConstType) { const int value = 42; const entt::meta_any any{&value}; auto deref = *any; ASSERT_TRUE(deref); ASSERT_DEATH(deref.cast() = 0, ""); } TEST(MetaPointerLike, DereferenceOperatorRawPointer) { int value = 0; entt::meta_any any{&value}; ASSERT_TRUE(any.type().is_pointer()); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_EQ(any.type(), entt::resolve()); 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()); deref.cast() = 42; ASSERT_EQ(*any.cast(), 42); ASSERT_EQ(value, 42); } TEST(MetaPointerLike, DereferenceOperatorSmartPointer) { auto value = std::make_shared(0); entt::meta_any any{value}; ASSERT_FALSE(any.type().is_pointer()); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_EQ(any.type(), entt::resolve>()); 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()); deref.cast() = 42; ASSERT_EQ(*any.cast>(), 42); ASSERT_EQ(*value, 42); } TEST(MetaPointerLike, PointerToConstMoveOnlyType) { const not_copyable_t instance; entt::meta_any any{&instance}; auto deref = *any; ASSERT_TRUE(any); ASSERT_TRUE(deref); ASSERT_EQ(deref.try_cast(), nullptr); ASSERT_NE(deref.try_cast(), nullptr); ASSERT_EQ(&deref.cast(), &instance); } TEST(MetaPointerLike, AsRef) { int value = 0; int *ptr = &value; entt::meta_any any{entt::forward_as_meta(ptr)}; ASSERT_TRUE(any.type().is_pointer()); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_EQ(any.type(), entt::resolve()); 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()); deref.cast() = 42; ASSERT_EQ(*any.cast(), 42); ASSERT_EQ(value, 42); } TEST(MetaPointerLike, AsConstRef) { int value = 42; int *const ptr = &value; entt::meta_any any{entt::forward_as_meta(ptr)}; ASSERT_TRUE(any.type().is_pointer()); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_EQ(any.type(), entt::resolve()); 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()); deref.cast() = 42; ASSERT_EQ(*any.cast(), 42); ASSERT_EQ(value, 42); } TEST(MetaPointerLike, DereferenceOverload) { 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()); ASSERT_EQ(deref.cast(), 42); ASSERT_EQ(deref.cast(), 42); }; test(adl_wrapped_shared_ptr{42}); test(spec_wrapped_shared_ptr{42}); } TEST(MetaPointerLike, DereferencePointerToConstOverload) { 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()); ASSERT_EQ(deref.cast(), 42); }; test(adl_wrapped_shared_ptr{42}); test(spec_wrapped_shared_ptr{42}); } ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferencePointerToConstOverload) { auto test = [](entt::meta_any any) { auto deref = *any; ASSERT_TRUE(deref); ASSERT_DEATH(deref.cast() = 42, ""); }; test(adl_wrapped_shared_ptr{42}); test(spec_wrapped_shared_ptr{42}); } TEST(MetaPointerLike, DereferencePointerToVoid) { auto test = [](entt::meta_any any) { ASSERT_TRUE(any.type().is_pointer()); ASSERT_TRUE(any.type().is_pointer_like()); auto deref = *any; ASSERT_FALSE(deref); }; test(static_cast(nullptr)); test(static_cast(nullptr)); } TEST(MetaPointerLike, DereferenceSmartPointerToVoid) { 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{}); test(std::unique_ptr{nullptr, nullptr}); } TEST(MetaPointerLike, DereferencePointerToFunction) { auto test = [](entt::meta_any any) { ASSERT_TRUE(any.type().is_pointer()); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_NE(any.try_cast(), nullptr); ASSERT_EQ(any.cast()(), 42); }; entt::meta_any func{&test_function}; test(func); test(*func); test(**func); test(*std::as_const(func)); } TEST(MetaPointerLike, DereferenceSelfPointer) { self_ptr obj{42}; entt::meta_any any{entt::forward_as_meta(obj)}; entt::meta_any deref = *any; ASSERT_TRUE(deref); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_EQ(deref.cast().value, obj.value); ASSERT_FALSE(deref.try_cast()); } TEST(MetaPointerLike, DereferenceProxyPointer) { int value = 3; proxy_ptr obj{value}; entt::meta_any any{obj}; entt::meta_any deref = *any; ASSERT_TRUE(deref); ASSERT_TRUE(any.type().is_pointer_like()); ASSERT_EQ(*deref.cast().value, value); ASSERT_TRUE(deref.try_cast()); *deref.cast().value = 42; ASSERT_EQ(value, 42); } TEST(MetaPointerLike, DereferenceArray) { entt::meta_any array{std::in_place_type}; entt::meta_any array_of_array{std::in_place_type}; ASSERT_EQ(array.type(), entt::resolve()); ASSERT_EQ(array_of_array.type(), entt::resolve()); ASSERT_FALSE(*array); ASSERT_FALSE(*array_of_array); } TEST(MetaPointerLike, DereferenceVerifiableNullPointerLike) { auto test = [](entt::meta_any any) { ASSERT_TRUE(any); ASSERT_FALSE(*any); }; test(entt::meta_any{static_cast(nullptr)}); test(entt::meta_any{std::shared_ptr{}}); test(entt::meta_any{std::unique_ptr{}}); }