#include #include #include #include #include #include #include #include "../common/config.h" template struct common_type: Base { void incr() { entt::poly_call<0>(*this); } void set(int v) { entt::poly_call<1>(*this, v); } int get() const { return entt::poly_call<2>(*this); } void decr() { entt::poly_call<3>(*this); } int mul(int v) const { return entt::poly_call<4>(*this, v); } int rand() const { return entt::poly_call<5>(*this); } }; template struct common_members { static void decr(Type &self) { self.set(self.get() - 1); } static double mul(const Type &self, double v) { return v * self.get(); } }; static int absolutely_random() { return 42; } template using common_impl = entt::value_list< &Type::incr, &Type::set, &Type::get, &common_members::decr, &common_members::mul, &absolutely_random>; struct Deduced : entt::type_list<> { template using type = common_type; template using members = common_members; template using impl = common_impl; }; struct Defined : entt::type_list< void(), void(int), int() const, void(), int(int) const, int() const> { template using type = common_type; template using members = common_members; template using impl = common_impl; }; struct DeducedEmbedded : entt::type_list<> { template struct type: Base { int get() const { return entt::poly_call<0>(*this); } }; template using impl = entt::value_list<&Type::get>; }; struct DefinedEmbedded : entt::type_list { template struct type: Base { // non-const get on purpose int get() { return entt::poly_call<0>(*this); } }; template using impl = entt::value_list<&Type::get>; }; struct impl { impl() = default; impl(int v) : value{v} {} void incr() { ++value; } void set(int v) { value = v; } int get() const { return value; } int value{}; }; struct alignas(64u) over_aligned: impl {}; template struct Poly: testing::Test { template using type = entt::basic_poly; }; template using PolyDeathTest = Poly; using PolyTypes = ::testing::Types; TYPED_TEST_SUITE(Poly, PolyTypes, ); TYPED_TEST_SUITE(PolyDeathTest, PolyTypes, ); template struct PolyEmbedded: testing::Test { using type = entt::basic_poly; }; using PolyEmbeddedTypes = ::testing::Types; TYPED_TEST_SUITE(PolyEmbedded, PolyEmbeddedTypes, ); TYPED_TEST(Poly, Functionalities) { using poly_type = typename TestFixture::template type<>; impl instance{}; poly_type empty{}; poly_type in_place{std::in_place_type, 3}; poly_type alias{std::in_place_type, instance}; poly_type value{impl{}}; ASSERT_FALSE(empty); ASSERT_TRUE(in_place); ASSERT_TRUE(alias); ASSERT_TRUE(value); ASSERT_EQ(empty.type(), entt::type_id()); ASSERT_EQ(in_place.type(), entt::type_id()); ASSERT_EQ(alias.type(), entt::type_id()); ASSERT_EQ(value.type(), entt::type_id()); ASSERT_EQ(alias.data(), &instance); ASSERT_EQ(std::as_const(alias).data(), &instance); ASSERT_EQ(value->rand(), 42); empty = impl{}; ASSERT_TRUE(empty); ASSERT_NE(empty.data(), nullptr); ASSERT_NE(std::as_const(empty).data(), nullptr); ASSERT_EQ(empty.type(), entt::type_id()); ASSERT_EQ(empty->get(), 0); empty.template emplace(3); ASSERT_TRUE(empty); ASSERT_EQ(std::as_const(empty)->get(), 3); poly_type ref = in_place.as_ref(); ASSERT_TRUE(ref); ASSERT_NE(ref.data(), nullptr); ASSERT_EQ(ref.data(), in_place.data()); ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data()); ASSERT_EQ(ref.type(), entt::type_id()); ASSERT_EQ(ref->get(), 3); poly_type null{}; std::swap(empty, null); ASSERT_FALSE(empty); poly_type copy = in_place; ASSERT_TRUE(copy); ASSERT_EQ(copy->get(), 3); poly_type move = std::move(copy); ASSERT_TRUE(move); ASSERT_TRUE(copy); ASSERT_EQ(move->get(), 3); move.reset(); ASSERT_FALSE(move); ASSERT_EQ(move.type(), entt::type_id()); } TYPED_TEST(PolyEmbedded, EmbeddedVtable) { using poly_type = typename TestFixture::type; poly_type poly{impl{}}; auto *ptr = static_cast(poly.data()); ASSERT_TRUE(poly); ASSERT_NE(poly.data(), nullptr); ASSERT_NE(std::as_const(poly).data(), nullptr); ASSERT_EQ(poly->get(), 0); ptr->value = 2; ASSERT_EQ(poly->get(), 2); } TYPED_TEST(Poly, Owned) { using poly_type = typename TestFixture::template type<>; poly_type poly{impl{}}; auto *ptr = static_cast(poly.data()); ASSERT_TRUE(poly); ASSERT_NE(poly.data(), nullptr); ASSERT_NE(std::as_const(poly).data(), nullptr); ASSERT_EQ(ptr->value, 0); ASSERT_EQ(poly->get(), 0); poly->set(1); poly->incr(); ASSERT_EQ(ptr->value, 2); ASSERT_EQ(std::as_const(poly)->get(), 2); ASSERT_EQ(poly->mul(3), 6); poly->decr(); ASSERT_EQ(ptr->value, 1); ASSERT_EQ(poly->get(), 1); ASSERT_EQ(poly->mul(3), 3); } TYPED_TEST(Poly, Reference) { using poly_type = typename TestFixture::template type<>; impl instance{}; poly_type poly{std::in_place_type, instance}; ASSERT_TRUE(poly); ASSERT_NE(poly.data(), nullptr); ASSERT_NE(std::as_const(poly).data(), nullptr); ASSERT_EQ(instance.value, 0); ASSERT_EQ(poly->get(), 0); poly->set(1); poly->incr(); ASSERT_EQ(instance.value, 2); ASSERT_EQ(std::as_const(poly)->get(), 2); ASSERT_EQ(poly->mul(3), 6); poly->decr(); ASSERT_EQ(instance.value, 1); ASSERT_EQ(poly->get(), 1); ASSERT_EQ(poly->mul(3), 3); } ENTT_DEBUG_TYPED_TEST(Poly, ConstReference) { using poly_type = typename TestFixture::template type<>; impl instance{}; poly_type poly{std::in_place_type, instance}; ASSERT_TRUE(poly); ASSERT_EQ(poly.data(), nullptr); ASSERT_NE(std::as_const(poly).data(), nullptr); ASSERT_EQ(instance.value, 0); ASSERT_EQ(poly->get(), 0); ASSERT_EQ(instance.value, 0); ASSERT_EQ(std::as_const(poly)->get(), 0); ASSERT_EQ(poly->mul(3), 0); ASSERT_EQ(instance.value, 0); ASSERT_EQ(poly->get(), 0); ASSERT_EQ(poly->mul(3), 0); } TYPED_TEST(PolyDeathTest, ConstReference) { using poly_type = typename TestFixture::template type<>; impl instance{}; poly_type poly{std::in_place_type, instance}; ASSERT_TRUE(poly); ASSERT_DEATH(poly->set(1), ""); } TYPED_TEST(Poly, AsRef) { using poly_type = typename TestFixture::template type<>; poly_type poly{impl{}}; auto ref = poly.as_ref(); auto cref = std::as_const(poly).as_ref(); ASSERT_NE(poly.data(), nullptr); ASSERT_NE(ref.data(), nullptr); ASSERT_EQ(cref.data(), nullptr); ASSERT_NE(std::as_const(cref).data(), nullptr); std::swap(ref, cref); ASSERT_EQ(ref.data(), nullptr); ASSERT_NE(std::as_const(ref).data(), nullptr); ASSERT_NE(cref.data(), nullptr); ref = ref.as_ref(); cref = std::as_const(cref).as_ref(); ASSERT_EQ(ref.data(), nullptr); ASSERT_NE(std::as_const(ref).data(), nullptr); ASSERT_EQ(cref.data(), nullptr); ASSERT_NE(std::as_const(cref).data(), nullptr); ref = impl{}; cref = impl{}; ASSERT_NE(ref.data(), nullptr); ASSERT_NE(cref.data(), nullptr); } TYPED_TEST(Poly, SBOVsZeroedSBOSize) { using poly_type = typename TestFixture::template type<>; using zeroed_type = typename TestFixture::template type<0u>; poly_type poly{impl{}}; const auto broken = poly.data(); poly_type other = std::move(poly); ASSERT_NE(broken, other.data()); zeroed_type dyn{impl{}}; const auto valid = dyn.data(); zeroed_type same = std::move(dyn); ASSERT_EQ(valid, same.data()); // everything works as expected same->incr(); ASSERT_EQ(same->get(), 1); } TYPED_TEST(Poly, SboAlignment) { static constexpr auto alignment = alignof(over_aligned); typename TestFixture::template type sbo[2]{over_aligned{}, over_aligned{}}; const auto *data = sbo[0].data(); ASSERT_TRUE((reinterpret_cast(sbo[0u].data()) % alignment) == 0u); ASSERT_TRUE((reinterpret_cast(sbo[1u].data()) % alignment) == 0u); std::swap(sbo[0], sbo[1]); ASSERT_TRUE((reinterpret_cast(sbo[0u].data()) % alignment) == 0u); ASSERT_TRUE((reinterpret_cast(sbo[1u].data()) % alignment) == 0u); ASSERT_NE(data, sbo[1].data()); } TYPED_TEST(Poly, NoSboAlignment) { static constexpr auto alignment = alignof(over_aligned); typename TestFixture::template type nosbo[2]{over_aligned{}, over_aligned{}}; const auto *data = nosbo[0].data(); ASSERT_TRUE((reinterpret_cast(nosbo[0u].data()) % alignment) == 0u); ASSERT_TRUE((reinterpret_cast(nosbo[1u].data()) % alignment) == 0u); std::swap(nosbo[0], nosbo[1]); ASSERT_TRUE((reinterpret_cast(nosbo[0u].data()) % alignment) == 0u); ASSERT_TRUE((reinterpret_cast(nosbo[1u].data()) % alignment) == 0u); ASSERT_EQ(data, nosbo[1].data()); }