#include #include #include #include #include #include #include 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_DEATH(deref.cast() = 0, ""); ASSERT_EQ(deref.cast(), 42); } TEST(MetaPointerLike, DereferenceOperatorConstAny) { auto test = [](const entt::meta_any any) { 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_DEATH(deref.cast() = 0, ""); ASSERT_EQ(deref.cast(), 42); }; int value = 42; test(&value); test(&std::as_const(value)); } 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{std::ref(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 * ptr = &value; entt::meta_any any{std::cref(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_DEATH(deref.cast() = 42, ""); ASSERT_EQ(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()); ASSERT_EQ(any.type().remove_pointer(), entt::resolve()); 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_EQ(any.type().remove_pointer(), entt::resolve()); ASSERT_NE(any.try_cast(), nullptr); ASSERT_EQ(any.cast()(), 42); }; test(entt::meta_any{&test_function}); test(*entt::meta_any{&test_function}); test(**entt::meta_any{&test_function}); } TEST(MetaPointerLike, DereferenceSelfPointer) { self_ptr obj{42}; entt::meta_any any{std::ref(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); }