#include #include #include #include #include #include #include #include #include #include #include #include "../common/config.h" struct empty { ~empty() { ++counter; } inline static int counter = 0; }; struct fat { fat(double v1, double v2, double v3, double v4) : value{v1, v2, v3, v4} {} ~fat() { ++counter; } bool operator==(const fat &other) const { return std::equal(std::begin(value), std::end(value), std::begin(other.value), std::end(other.value)); } inline static int counter{0}; double value[4]; }; struct not_comparable { bool operator==(const not_comparable &) const = delete; }; struct not_copyable { not_copyable() : payload{} {} not_copyable(const not_copyable &) = delete; not_copyable(not_copyable &&) = default; not_copyable &operator=(const not_copyable &) = delete; not_copyable &operator=(not_copyable &&) = default; double payload; }; struct not_movable { not_movable() = default; not_movable(const not_movable &) = default; not_movable(not_movable &&) = delete; not_movable &operator=(const not_movable &) = default; not_movable &operator=(not_movable &&) = delete; double payload{}; }; struct alignas(64u) over_aligned {}; struct Any: ::testing::Test { void SetUp() override { fat::counter = 0; empty::counter = 0; } }; using AnyDeathTest = Any; TEST_F(Any, SBO) { entt::any any{'c'}; ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(any), 'c'); } TEST_F(Any, NoSBO) { fat instance{.1, .2, .3, .4}; entt::any any{instance}; ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(any), instance); } TEST_F(Any, Empty) { entt::any any{}; ASSERT_FALSE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(any.data(), nullptr); } TEST_F(Any, SBOInPlaceTypeConstruction) { entt::any any{std::in_place_type, 42}; ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(any), 42); auto other = any.as_ref(); ASSERT_TRUE(other); ASSERT_FALSE(other.owner()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(other), 42); ASSERT_EQ(other.data(), any.data()); } TEST_F(Any, SBOAsRefConstruction) { int value = 42; entt::any any{entt::forward_as_any(value)}; ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&any), &value); ASSERT_EQ(entt::any_cast(&any), &value); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &value); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &value); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(any.data(), &value); ASSERT_EQ(std::as_const(any).data(), &value); any.emplace(value); ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), &value); auto other = any.as_ref(); ASSERT_TRUE(other); ASSERT_FALSE(other.owner()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(other), 42); ASSERT_EQ(other.data(), any.data()); } TEST_F(Any, SBOAsConstRefConstruction) { const int value = 42; entt::any any{entt::forward_as_any(value)}; ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&any), &value); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &value); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &value); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(any.data(), nullptr); ASSERT_EQ(std::as_const(any).data(), &value); any.emplace(value); ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), &value); auto other = any.as_ref(); ASSERT_TRUE(other); ASSERT_FALSE(other.owner()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(other), 42); ASSERT_EQ(other.data(), any.data()); } TEST_F(Any, SBOCopyConstruction) { entt::any any{42}; entt::any other{any}; ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), 42); } TEST_F(Any, SBOCopyAssignment) { entt::any any{42}; entt::any other{3}; other = any; ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), 42); } TEST_F(Any, SBOMoveConstruction) { entt::any any{42}; entt::any other{std::move(any)}; ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_NE(any.data(), nullptr); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), 42); } TEST_F(Any, SBOMoveAssignment) { entt::any any{42}; entt::any other{3}; other = std::move(any); ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_NE(any.data(), nullptr); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), 42); } TEST_F(Any, SBODirectAssignment) { entt::any any{}; any = 42; ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(any), 42); } TEST_F(Any, SBOAssignValue) { entt::any any{42}; entt::any other{3}; entt::any invalid{'c'}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_TRUE(any.assign(other)); ASSERT_FALSE(any.assign(invalid)); ASSERT_EQ(entt::any_cast(any), 3); } TEST_F(Any, SBOAsRefAssignValue) { int value = 42; entt::any any{entt::forward_as_any(value)}; entt::any other{3}; entt::any invalid{'c'}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_TRUE(any.assign(other)); ASSERT_FALSE(any.assign(invalid)); ASSERT_EQ(entt::any_cast(any), 3); ASSERT_EQ(value, 3); } TEST_F(Any, SBOAsConstRefAssignValue) { const int value = 42; entt::any any{entt::forward_as_any(value)}; entt::any other{3}; entt::any invalid{'c'}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_FALSE(any.assign(other)); ASSERT_FALSE(any.assign(invalid)); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(value, 42); } TEST_F(Any, SBOTransferValue) { entt::any any{42}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_TRUE(any.assign(3)); ASSERT_FALSE(any.assign('c')); ASSERT_EQ(entt::any_cast(any), 3); } TEST_F(Any, SBOTransferConstValue) { const int value = 3; entt::any any{42}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_TRUE(any.assign(entt::forward_as_any(value))); ASSERT_EQ(entt::any_cast(any), 3); } TEST_F(Any, SBOAsRefTransferValue) { int value = 42; entt::any any{entt::forward_as_any(value)}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_TRUE(any.assign(3)); ASSERT_FALSE(any.assign('c')); ASSERT_EQ(entt::any_cast(any), 3); ASSERT_EQ(value, 3); } TEST_F(Any, SBOAsConstRefTransferValue) { const int value = 42; entt::any any{entt::forward_as_any(value)}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_FALSE(any.assign(3)); ASSERT_FALSE(any.assign('c')); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(value, 42); } TEST_F(Any, NoSBOInPlaceTypeConstruction) { fat instance{.1, .2, .3, .4}; entt::any any{std::in_place_type, instance}; ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(any), instance); auto other = any.as_ref(); ASSERT_TRUE(other); ASSERT_FALSE(other.owner()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(other), (fat{.1, .2, .3, .4})); ASSERT_EQ(other.data(), any.data()); } TEST_F(Any, NoSBOAsRefConstruction) { fat instance{.1, .2, .3, .4}; entt::any any{entt::forward_as_any(instance)}; ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&any), &instance); ASSERT_EQ(entt::any_cast(&any), &instance); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &instance); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &instance); ASSERT_EQ(entt::any_cast(any), instance); ASSERT_EQ(entt::any_cast(any), instance); ASSERT_EQ(any.data(), &instance); ASSERT_EQ(std::as_const(any).data(), &instance); any.emplace(instance); ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), &instance); auto other = any.as_ref(); ASSERT_TRUE(other); ASSERT_FALSE(other.owner()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(other), (fat{.1, .2, .3, .4})); ASSERT_EQ(other.data(), any.data()); } TEST_F(Any, NoSBOAsConstRefConstruction) { const fat instance{.1, .2, .3, .4}; entt::any any{entt::forward_as_any(instance)}; ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&any), &instance); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &instance); ASSERT_EQ(entt::any_cast(&std::as_const(any)), &instance); ASSERT_EQ(entt::any_cast(any), instance); ASSERT_EQ(entt::any_cast(any), instance); ASSERT_EQ(any.data(), nullptr); ASSERT_EQ(std::as_const(any).data(), &instance); any.emplace(instance); ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), &instance); auto other = any.as_ref(); ASSERT_TRUE(other); ASSERT_FALSE(other.owner()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(other), (fat{.1, .2, .3, .4})); ASSERT_EQ(other.data(), any.data()); } TEST_F(Any, NoSBOCopyConstruction) { fat instance{.1, .2, .3, .4}; entt::any any{instance}; entt::any other{any}; ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), instance); } TEST_F(Any, NoSBOCopyAssignment) { fat instance{.1, .2, .3, .4}; entt::any any{instance}; entt::any other{3}; other = any; ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), instance); } TEST_F(Any, NoSBOMoveConstruction) { fat instance{.1, .2, .3, .4}; entt::any any{instance}; entt::any other{std::move(any)}; ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.data(), nullptr); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), instance); } TEST_F(Any, NoSBOMoveAssignment) { fat instance{.1, .2, .3, .4}; entt::any any{instance}; entt::any other{3}; other = std::move(any); ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.data(), nullptr); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&other), nullptr); ASSERT_EQ(entt::any_cast(other), instance); } TEST_F(Any, NoSBODirectAssignment) { fat instance{.1, .2, .3, .4}; entt::any any{}; any = instance; ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(any), instance); } TEST_F(Any, NoSBOAssignValue) { entt::any any{fat{.1, .2, .3, .4}}; entt::any other{fat{.0, .1, .2, .3}}; entt::any invalid{'c'}; const void *addr = std::as_const(any).data(); ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_TRUE(any.assign(other)); ASSERT_FALSE(any.assign(invalid)); ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3})); ASSERT_EQ(addr, std::as_const(any).data()); } TEST_F(Any, NoSBOAsRefAssignValue) { fat instance{.1, .2, .3, .4}; entt::any any{entt::forward_as_any(instance)}; entt::any other{fat{.0, .1, .2, .3}}; entt::any invalid{'c'}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_TRUE(any.assign(other)); ASSERT_FALSE(any.assign(invalid)); ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3})); ASSERT_EQ(instance, (fat{.0, .1, .2, .3})); } TEST_F(Any, NoSBOAsConstRefAssignValue) { const fat instance{.1, .2, .3, .4}; entt::any any{entt::forward_as_any(instance)}; entt::any other{fat{.0, .1, .2, .3}}; entt::any invalid{'c'}; ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_FALSE(any.assign(other)); ASSERT_FALSE(any.assign(invalid)); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_EQ(instance, (fat{.1, .2, .3, .4})); } TEST_F(Any, NoSBOTransferValue) { entt::any any{fat{.1, .2, .3, .4}}; const void *addr = std::as_const(any).data(); ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_TRUE(any.assign(fat{.0, .1, .2, .3})); ASSERT_FALSE(any.assign('c')); ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3})); ASSERT_EQ(addr, std::as_const(any).data()); } TEST_F(Any, NoSBOTransferConstValue) { const fat instance{.0, .1, .2, .3}; entt::any any{fat{.1, .2, .3, .4}}; const void *addr = std::as_const(any).data(); ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_TRUE(any.assign(entt::forward_as_any(instance))); ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3})); ASSERT_EQ(addr, std::as_const(any).data()); } TEST_F(Any, NoSBOAsRefTransferValue) { fat instance{.1, .2, .3, .4}; entt::any any{entt::forward_as_any(instance)}; const void *addr = std::as_const(any).data(); ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_TRUE(any.assign(fat{.0, .1, .2, .3})); ASSERT_FALSE(any.assign('c')); ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3})); ASSERT_EQ(instance, (fat{.0, .1, .2, .3})); ASSERT_EQ(addr, std::as_const(any).data()); } TEST_F(Any, NoSBOAsConstRefTransferValue) { const fat instance{.1, .2, .3, .4}; entt::any any{entt::forward_as_any(instance)}; const void *addr = std::as_const(any).data(); ASSERT_TRUE(any); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_FALSE(any.assign(fat{.0, .1, .2, .3})); ASSERT_FALSE(any.assign('c')); ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4})); ASSERT_EQ(instance, (fat{.1, .2, .3, .4})); ASSERT_EQ(addr, std::as_const(any).data()); } TEST_F(Any, VoidInPlaceTypeConstruction) { entt::any any{std::in_place_type}; ASSERT_FALSE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); } TEST_F(Any, VoidCopyConstruction) { entt::any any{std::in_place_type}; entt::any other{any}; ASSERT_FALSE(any); ASSERT_FALSE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&other), nullptr); } TEST_F(Any, VoidCopyAssignment) { entt::any any{std::in_place_type}; entt::any other{42}; other = any; ASSERT_FALSE(any); ASSERT_FALSE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&other), nullptr); } TEST_F(Any, VoidMoveConstruction) { entt::any any{std::in_place_type}; entt::any other{std::move(any)}; ASSERT_FALSE(any); ASSERT_FALSE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&other), nullptr); } TEST_F(Any, VoidMoveAssignment) { entt::any any{std::in_place_type}; entt::any other{42}; other = std::move(any); ASSERT_FALSE(any); ASSERT_FALSE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(other.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&other), nullptr); } TEST_F(Any, SBOMoveValidButUnspecifiedState) { entt::any any{42}; entt::any other{std::move(any)}; entt::any valid = std::move(other); ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(valid); } TEST_F(Any, NoSBOMoveValidButUnspecifiedState) { fat instance{.1, .2, .3, .4}; entt::any any{instance}; entt::any other{std::move(any)}; entt::any valid = std::move(other); ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(valid); } TEST_F(Any, VoidMoveValidButUnspecifiedState) { entt::any any{std::in_place_type}; entt::any other{std::move(any)}; entt::any valid = std::move(other); ASSERT_FALSE(any); ASSERT_FALSE(other); ASSERT_FALSE(valid); } TEST_F(Any, SBODestruction) { { entt::any any{std::in_place_type}; any.emplace(); any = empty{}; entt::any other{std::move(any)}; any = std::move(other); } ASSERT_EQ(empty::counter, 6); } TEST_F(Any, NoSBODestruction) { { entt::any any{std::in_place_type, 1., 2., 3., 4.}; any.emplace(1., 2., 3., 4.); any = fat{1., 2., 3., 4.}; entt::any other{std::move(any)}; any = std::move(other); } ASSERT_EQ(fat::counter, 4); } TEST_F(Any, VoidDestruction) { // just let asan tell us if everything is ok here [[maybe_unused]] entt::any any{std::in_place_type}; } TEST_F(Any, Emplace) { entt::any any{}; any.emplace(42); ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(any), 42); } TEST_F(Any, EmplaceVoid) { entt::any any{}; any.emplace(); ASSERT_FALSE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); } TEST_F(Any, Reset) { entt::any any{42}; ASSERT_TRUE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); any.reset(); ASSERT_FALSE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); int value = 42; any.emplace(value); ASSERT_TRUE(any); ASSERT_FALSE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); any.reset(); ASSERT_FALSE(any); ASSERT_TRUE(any.owner()); ASSERT_EQ(any.type(), entt::type_id()); } TEST_F(Any, SBOSwap) { entt::any lhs{'c'}; entt::any rhs{42}; std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), 42); ASSERT_EQ(entt::any_cast(rhs), 'c'); } TEST_F(Any, NoSBOSwap) { entt::any lhs{fat{.1, .2, .3, .4}}; entt::any rhs{fat{.4, .3, .2, .1}}; std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(entt::any_cast(lhs), (fat{.4, .3, .2, .1})); ASSERT_EQ(entt::any_cast(rhs), (fat{.1, .2, .3, .4})); } TEST_F(Any, VoidSwap) { entt::any lhs{std::in_place_type}; entt::any rhs{std::in_place_type}; const auto *pre = lhs.data(); std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(pre, lhs.data()); } TEST_F(Any, SBOWithNoSBOSwap) { entt::any lhs{fat{.1, .2, .3, .4}}; entt::any rhs{'c'}; std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), 'c'); ASSERT_EQ(entt::any_cast(rhs), (fat{.1, .2, .3, .4})); } TEST_F(Any, SBOWithRefSwap) { int value = 3; entt::any lhs{entt::forward_as_any(value)}; entt::any rhs{'c'}; std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_FALSE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), 'c'); ASSERT_EQ(entt::any_cast(rhs), 3); ASSERT_EQ(rhs.data(), &value); } TEST_F(Any, SBOWithConstRefSwap) { const int value = 3; entt::any lhs{entt::forward_as_any(value)}; entt::any rhs{'c'}; std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_FALSE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), 'c'); ASSERT_EQ(entt::any_cast(rhs), 3); ASSERT_EQ(rhs.data(), nullptr); ASSERT_EQ(std::as_const(rhs).data(), &value); } TEST_F(Any, SBOWithEmptySwap) { entt::any lhs{'c'}; entt::any rhs{}; std::swap(lhs, rhs); ASSERT_FALSE(lhs); ASSERT_TRUE(lhs.owner()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(rhs), 'c'); std::swap(lhs, rhs); ASSERT_FALSE(rhs); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), 'c'); } TEST_F(Any, SBOWithVoidSwap) { entt::any lhs{'c'}; entt::any rhs{std::in_place_type}; std::swap(lhs, rhs); ASSERT_FALSE(lhs); ASSERT_TRUE(lhs.owner()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(rhs), 'c'); std::swap(lhs, rhs); ASSERT_FALSE(rhs); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), 'c'); } TEST_F(Any, NoSBOWithRefSwap) { int value = 3; entt::any lhs{entt::forward_as_any(value)}; entt::any rhs{fat{.1, .2, .3, .4}}; std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_FALSE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), (fat{.1, .2, .3, .4})); ASSERT_EQ(entt::any_cast(rhs), 3); ASSERT_EQ(rhs.data(), &value); } TEST_F(Any, NoSBOWithConstRefSwap) { const int value = 3; entt::any lhs{entt::forward_as_any(value)}; entt::any rhs{fat{.1, .2, .3, .4}}; std::swap(lhs, rhs); ASSERT_TRUE(lhs.owner()); ASSERT_FALSE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), (fat{.1, .2, .3, .4})); ASSERT_EQ(entt::any_cast(rhs), 3); ASSERT_EQ(rhs.data(), nullptr); ASSERT_EQ(std::as_const(rhs).data(), &value); } TEST_F(Any, NoSBOWithEmptySwap) { entt::any lhs{fat{.1, .2, .3, .4}}; entt::any rhs{}; std::swap(lhs, rhs); ASSERT_FALSE(lhs); ASSERT_TRUE(lhs.owner()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(rhs), (fat{.1, .2, .3, .4})); std::swap(lhs, rhs); ASSERT_FALSE(rhs); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), (fat{.1, .2, .3, .4})); } TEST_F(Any, NoSBOWithVoidSwap) { entt::any lhs{fat{.1, .2, .3, .4}}; entt::any rhs{std::in_place_type}; std::swap(lhs, rhs); ASSERT_FALSE(lhs); ASSERT_TRUE(lhs.owner()); ASSERT_EQ(rhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(rhs), (fat{.1, .2, .3, .4})); std::swap(lhs, rhs); ASSERT_FALSE(rhs); ASSERT_TRUE(rhs.owner()); ASSERT_EQ(lhs.type(), entt::type_id()); ASSERT_EQ(entt::any_cast(&lhs), nullptr); ASSERT_EQ(entt::any_cast(&rhs), nullptr); ASSERT_EQ(entt::any_cast(lhs), (fat{.1, .2, .3, .4})); } TEST_F(Any, AsRef) { entt::any any{42}; auto ref = any.as_ref(); auto cref = std::as_const(any).as_ref(); ASSERT_FALSE(ref.owner()); ASSERT_FALSE(cref.owner()); ASSERT_EQ(entt::any_cast(&any), any.data()); ASSERT_EQ(entt::any_cast(&ref), any.data()); ASSERT_EQ(entt::any_cast(&cref), nullptr); ASSERT_EQ(entt::any_cast(&any), any.data()); ASSERT_EQ(entt::any_cast(&ref), any.data()); ASSERT_EQ(entt::any_cast(&cref), any.data()); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(entt::any_cast(cref), 42); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(entt::any_cast(cref), 42); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(entt::any_cast(&cref), nullptr); ASSERT_EQ(entt::any_cast(cref), 42); entt::any_cast(any) = 3; ASSERT_EQ(entt::any_cast(any), 3); ASSERT_EQ(entt::any_cast(ref), 3); ASSERT_EQ(entt::any_cast(cref), 3); std::swap(ref, cref); ASSERT_FALSE(ref.owner()); ASSERT_FALSE(cref.owner()); ASSERT_EQ(entt::any_cast(&ref), nullptr); ASSERT_EQ(entt::any_cast(&cref), any.data()); ref = ref.as_ref(); cref = std::as_const(cref).as_ref(); ASSERT_FALSE(ref.owner()); ASSERT_FALSE(cref.owner()); ASSERT_EQ(entt::any_cast(&ref), nullptr); ASSERT_EQ(entt::any_cast(&cref), nullptr); ASSERT_EQ(entt::any_cast(&ref), any.data()); ASSERT_EQ(entt::any_cast(&cref), any.data()); ASSERT_EQ(entt::any_cast(&ref), nullptr); ASSERT_EQ(entt::any_cast(&cref), nullptr); ASSERT_EQ(entt::any_cast(ref), 3); ASSERT_EQ(entt::any_cast(cref), 3); ref = 42; cref = 42; ASSERT_TRUE(ref.owner()); ASSERT_TRUE(cref.owner()); ASSERT_NE(entt::any_cast(&ref), nullptr); ASSERT_NE(entt::any_cast(&cref), nullptr); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(entt::any_cast(cref), 42); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(entt::any_cast(cref), 42); ASSERT_NE(entt::any_cast(&ref), any.data()); ASSERT_NE(entt::any_cast(&cref), any.data()); } TEST_F(Any, Comparable) { auto test = [](entt::any any, entt::any other) { ASSERT_EQ(any, any); ASSERT_NE(other, any); ASSERT_NE(any, entt::any{}); ASSERT_TRUE(any == any); ASSERT_FALSE(other == any); ASSERT_TRUE(any != other); ASSERT_TRUE(entt::any{} != any); }; int value = 42; test('c', 'a'); test(fat{.1, .2, .3, .4}, fat{.0, .1, .2, .3}); test(entt::forward_as_any(value), 3); test(3, entt::make_any(value)); test('c', value); } TEST_F(Any, NotComparable) { auto test = [](const auto &instance) { auto any = entt::forward_as_any(instance); ASSERT_EQ(any, any); ASSERT_NE(any, entt::any{instance}); ASSERT_NE(entt::any{}, any); ASSERT_TRUE(any == any); ASSERT_FALSE(any == entt::any{instance}); ASSERT_TRUE(entt::any{} != any); }; test(not_comparable{}); test(std::unordered_map{}); test(std::vector{}); } TEST_F(Any, CompareVoid) { entt::any any{std::in_place_type}; ASSERT_EQ(any, any); ASSERT_EQ(any, entt::any{std::in_place_type}); ASSERT_NE(entt::any{'a'}, any); ASSERT_EQ(any, entt::any{}); ASSERT_TRUE(any == any); ASSERT_TRUE(any == entt::any{std::in_place_type}); ASSERT_FALSE(entt::any{'a'} == any); ASSERT_TRUE(any != entt::any{'a'}); ASSERT_FALSE(entt::any{} != any); } TEST_F(Any, AnyCast) { entt::any any{42}; const auto &cany = any; ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&cany), nullptr); ASSERT_EQ(*entt::any_cast(&any), 42); ASSERT_EQ(*entt::any_cast(&cany), 42); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(cany), 42); not_copyable instance{}; instance.payload = 42.; entt::any ref{entt::forward_as_any(instance)}; entt::any cref{entt::forward_as_any(std::as_const(instance).payload)}; ASSERT_EQ(entt::any_cast(std::move(ref)).payload, 42.); ASSERT_EQ(entt::any_cast(std::move(cref)), 42.); ASSERT_EQ(entt::any_cast(entt::any{42}), 42); } ENTT_DEBUG_TEST_F(AnyDeathTest, AnyCast) { entt::any any{42}; const auto &cany = any; ASSERT_DEATH(entt::any_cast(any), ""); ASSERT_DEATH(entt::any_cast(cany), ""); not_copyable instance{}; instance.payload = 42.; entt::any ref{entt::forward_as_any(instance)}; entt::any cref{entt::forward_as_any(std::as_const(instance).payload)}; ASSERT_DEATH(entt::any_cast(std::as_const(ref).as_ref()), ""); ASSERT_DEATH(entt::any_cast(entt::any{42}), ""); } TEST_F(Any, MakeAny) { int value = 42; auto any = entt::make_any(value); auto ext = entt::make_any(value); auto ref = entt::make_any(value); ASSERT_TRUE(any); ASSERT_TRUE(ext); ASSERT_TRUE(ref); ASSERT_TRUE(any.owner()); ASSERT_TRUE(ext.owner()); ASSERT_FALSE(ref.owner()); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(ext), 42); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(decltype(any)::length, entt::any::length); ASSERT_NE(decltype(ext)::length, entt::any::length); ASSERT_EQ(decltype(ref)::length, entt::any::length); ASSERT_NE(any.data(), &value); ASSERT_NE(ext.data(), &value); ASSERT_EQ(ref.data(), &value); } TEST_F(Any, ForwardAsAny) { int value = 42; auto ref = entt::forward_as_any(value); auto cref = entt::forward_as_any(std::as_const(value)); auto any = entt::forward_as_any(std::move(value)); ASSERT_TRUE(any); ASSERT_TRUE(ref); ASSERT_TRUE(cref); ASSERT_TRUE(any.owner()); ASSERT_FALSE(ref.owner()); ASSERT_FALSE(cref.owner()); ASSERT_NE(entt::any_cast(&any), nullptr); ASSERT_NE(entt::any_cast(&ref), nullptr); ASSERT_EQ(entt::any_cast(&cref), nullptr); ASSERT_EQ(entt::any_cast(any), 42); ASSERT_EQ(entt::any_cast(ref), 42); ASSERT_EQ(entt::any_cast(cref), 42); ASSERT_NE(any.data(), &value); ASSERT_EQ(ref.data(), &value); } TEST_F(Any, NotCopyableType) { const not_copyable value{}; entt::any any{std::in_place_type}; entt::any other = entt::forward_as_any(value); ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_FALSE(other.owner()); ASSERT_EQ(any.type(), other.type()); ASSERT_FALSE(any.assign(other)); ASSERT_FALSE(any.assign(std::move(other))); entt::any copy{any}; ASSERT_TRUE(any); ASSERT_FALSE(copy); ASSERT_TRUE(any.owner()); ASSERT_TRUE(copy.owner()); copy = any; ASSERT_TRUE(any); ASSERT_FALSE(copy); ASSERT_TRUE(any.owner()); ASSERT_TRUE(copy.owner()); } TEST_F(Any, NotCopyableValueType) { std::vector vec{}; vec.emplace_back(std::in_place_type); vec.shrink_to_fit(); ASSERT_EQ(vec.size(), 1u); ASSERT_EQ(vec.capacity(), 1u); ASSERT_TRUE(vec[0u]); // strong exception guarantee due to noexcept move ctor vec.emplace_back(std::in_place_type); ASSERT_EQ(vec.size(), 2u); ASSERT_TRUE(vec[0u]); ASSERT_TRUE(vec[1u]); } TEST_F(Any, NotMovableType) { entt::any any{std::in_place_type}; entt::any other{std::in_place_type}; ASSERT_TRUE(any); ASSERT_TRUE(other); ASSERT_TRUE(any.owner()); ASSERT_TRUE(other.owner()); ASSERT_EQ(any.type(), other.type()); ASSERT_TRUE(any.assign(other)); ASSERT_TRUE(any.assign(std::move(other))); entt::any copy{any}; ASSERT_TRUE(any); ASSERT_TRUE(copy); ASSERT_TRUE(any.owner()); ASSERT_TRUE(copy.owner()); copy = any; ASSERT_TRUE(any); ASSERT_TRUE(copy); ASSERT_TRUE(any.owner()); ASSERT_TRUE(copy.owner()); } TEST_F(Any, Array) { entt::any any{std::in_place_type}; entt::any copy{any}; ASSERT_TRUE(any); ASSERT_FALSE(copy); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_NE(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&any), nullptr); ASSERT_EQ(entt::any_cast(&any), nullptr); entt::any_cast(any)[0] = 42; ASSERT_EQ(entt::any_cast(std::as_const(any))[0], 42); } TEST_F(Any, CopyMoveReference) { int value{}; auto test = [&](auto &&ref) { value = 3; auto any = entt::forward_as_any(ref); entt::any move = std::move(any); entt::any copy = move; ASSERT_TRUE(any); ASSERT_TRUE(move); ASSERT_TRUE(copy); ASSERT_FALSE(any.owner()); ASSERT_FALSE(move.owner()); ASSERT_TRUE(copy.owner()); ASSERT_EQ(move.type(), entt::type_id()); ASSERT_EQ(copy.type(), entt::type_id()); ASSERT_EQ(std::as_const(move).data(), &value); ASSERT_NE(std::as_const(copy).data(), &value); ASSERT_EQ(entt::any_cast(move), 3); ASSERT_EQ(entt::any_cast(copy), 3); value = 42; ASSERT_EQ(entt::any_cast(move), 42); ASSERT_EQ(entt::any_cast(copy), 3); }; test(value); test(std::as_const(value)); } TEST_F(Any, SBOVsZeroedSBOSize) { entt::any sbo{42}; const auto *broken = sbo.data(); entt::any other = std::move(sbo); ASSERT_NE(broken, other.data()); entt::basic_any<0u> dyn{42}; const auto *valid = dyn.data(); entt::basic_any<0u> same = std::move(dyn); ASSERT_EQ(valid, same.data()); } TEST_F(Any, SboAlignment) { static constexpr auto alignment = alignof(over_aligned); entt::basic_any 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()); } TEST_F(Any, NoSboAlignment) { static constexpr auto alignment = alignof(over_aligned); entt::basic_any 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()); } TEST_F(Any, AggregatesMustWork) { struct aggregate_type { int value; }; // the goal of this test is to enforce the requirements for aggregate types entt::any{std::in_place_type, 42}.emplace(42); } TEST_F(Any, DeducedArrayType) { entt::any any{"array of char"}; ASSERT_TRUE(any); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ((std::strcmp("array of char", entt::any_cast(any))), 0); any = "another array of char"; ASSERT_TRUE(any); ASSERT_EQ(any.type(), entt::type_id()); ASSERT_EQ((std::strcmp("another array of char", entt::any_cast(any))), 0); }