///////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ///////////////////////////////////////////////////////////////////////////// #include "EASTLTest.h" #include #include #include #ifdef EA_COMPILER_CPP14_ENABLED #include "ConceptImpls.h" #include int TestVariantAlternative() { using namespace eastl; int nErrorCount = 0; { using v_t = variant; static_assert(is_same_v, int>, "error variant_alternative"); } { using v_t = variant; static_assert(is_same_v, int>, "error variant_alternative"); static_assert(is_same_v, long>, "error variant_alternative"); static_assert(is_same_v, short>, "error variant_alternative"); static_assert(is_same_v, char>, "error variant_alternative"); } { struct custom_type1 {}; struct custom_type2 {}; struct custom_type3 {}; using v_t = variant; static_assert(is_same_v, unsigned>, "error variant_alternative"); static_assert(is_same_v, signed>, "error variant_alternative"); static_assert(is_same_v, custom_type1>, "error variant_alternative"); static_assert(is_same_v, custom_type2>, "error variant_alternative"); static_assert(is_same_v, custom_type3>, "error variant_alternative"); } // cv-qualifier tests { using v_t = variant; static_assert(is_same_v, int>, "error variant_alternative"); static_assert(is_same_v, const int>, "error variant_alternative"); static_assert(is_same_v, volatile int>, "error variant_alternative"); static_assert(is_same_v, const volatile int>, "error variant_alternative"); } return nErrorCount; } int TestVariantSize() { using namespace eastl; int nErrorCount = 0; static_assert(variant_size>() == 1, "error variant_size"); static_assert(variant_size>() == 2, "error variant_size"); static_assert(variant_size>() == 4, "error variant_size"); static_assert(variant_size>() == 1, "error variant_size"); static_assert(variant_size>() == 1, "error variant_size"); static_assert(variant_size>() == 1, "error variant_size"); static_assert(variant_size_v> == 1, "error variant_size"); static_assert(variant_size_v> == 2, "error variant_size"); static_assert(variant_size_v> == 4, "error variant_size"); static_assert(variant_size_v> == 1, "error variant_size"); static_assert(variant_size_v> == 1, "error variant_size"); static_assert(variant_size_v> == 1, "error variant_size"); static_assert(variant_size_v> == 2, "error variant_size_v"); static_assert(variant_size_v> == 2, "error variant_size_v"); static_assert(variant_size_v> == 3, "error variant_size_v"); return nErrorCount; } int TestVariantHash() { using namespace eastl; int nErrorCount = 0; { hash h; EA_UNUSED(h); } return nErrorCount; } int TestVariantBasic() { using namespace eastl; int nErrorCount = 0; { VERIFY(variant_npos == size_t(-1)); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { struct MyObj { MyObj() : i(1337) {} ~MyObj() {} int i; }; struct MyObj2 { MyObj2(int& ii) : i(ii) {} ~MyObj2() {} MyObj2& operator=(const MyObj2&) = delete; int& i; }; static_assert(!eastl::is_trivially_destructible_v, "MyObj can't be trivially destructible"); static_assert(!eastl::is_trivially_destructible_v, "MyObj2 can't be trivially destructible"); { eastl::variant myVar; VERIFY(get(myVar).i == 1337); } { eastl::variant myVar = MyObj(); VERIFY(get(myVar).i == 1337); } { int i = 42; eastl::variant myVar = MyObj2(i); VERIFY(get(myVar).i == 42); } { auto m = MyObj(); m.i = 2000; eastl::variant myVar = m; VERIFY(get(myVar).i == 2000); } } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { variant v; EA_UNUSED(v); } { // verify constructors and destructors are called { variant v = TestObject(1337); VERIFY((get(v)).mX == 1337); variant vCopy = v; VERIFY((get(vCopy)).mX == 1337); } VERIFY(TestObject::IsClear()); TestObject::Reset(); } { variant v; VERIFY(*(get_if(&v)) == ""); VERIFY(get_if(&v)->empty()); VERIFY(get_if(&v)->length() == 0); VERIFY(get_if(&v)->size() == 0); *(get_if(&v)) += 'a'; VERIFY(*(get_if(&v)) == "a"); } return nErrorCount; } int TestVariantGet() { using namespace eastl; int nErrorCount = 0; { const char* strValue = "canada"; using v_t = variant; { v_t v; v = 42; VERIFY(v.index() == 0); VERIFY(*get_if(&v) == 42); VERIFY(get(v) == 42); VERIFY( holds_alternative(v)); VERIFY(!holds_alternative(v)); } { v_t v; v = strValue; VERIFY(v.index() == 1); VERIFY(*get_if(&v) == strValue); VERIFY(get(v) == strValue); VERIFY(!holds_alternative(v)); VERIFY(holds_alternative(v)); VERIFY(get(move(v)) == strValue); } { v_t v; v = 42; VERIFY(v.index() == 0); VERIFY(*get_if<0>(&v) == 42); VERIFY(get<0>(v) == 42); VERIFY( holds_alternative(v)); VERIFY(!holds_alternative(v)); } { v_t v; v = strValue; VERIFY(v.index() == 1); VERIFY(*get_if<1>(&v) == strValue); VERIFY(get<1>(v) == strValue); VERIFY(!holds_alternative(v)); VERIFY( holds_alternative(v)); } { v_t v; v = strValue; VERIFY(v.index() == 1); VERIFY(*get_if<1>(&v) == strValue); VERIFY(get_if<0>(&v) == nullptr); } { VERIFY(get_if<0>((v_t*)nullptr) == nullptr); VERIFY(get_if<1>((v_t*)nullptr) == nullptr); } } return nErrorCount; } int TestVariantHoldsAlternative() { using namespace eastl; int nErrorCount = 0; { { using v_t = variant; // default construct first type v_t v; VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. VERIFY( holds_alternative(v)); // variant does hold an int, because its a default constructible first parameter VERIFY(!holds_alternative(v)); // variant does not hold a short } { using v_t = variant; // default construct monostate v_t v; VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. VERIFY(!holds_alternative(v)); // Verify that a query for a T not in the variant typelist returns false. VERIFY(!holds_alternative(v)); // variant does not hold an int VERIFY(!holds_alternative(v)); // variant does not hold a short } { using v_t = variant; { v_t v; VERIFY(!holds_alternative(v)); // variant does not hold an int v = 42; VERIFY(holds_alternative(v)); // variant does hold an int } { v_t v1, v2; VERIFY(!holds_alternative(v1)); VERIFY(!holds_alternative(v2)); v1 = 42; VERIFY(holds_alternative(v1)); VERIFY(!holds_alternative(v2)); eastl::swap(v1, v2); VERIFY(!holds_alternative(v1)); VERIFY(holds_alternative(v2)); } } } return nErrorCount; } int TestVariantValuelessByException() { using namespace eastl; int nErrorCount = 0; { { using v_t = variant; static_assert(eastl::is_default_constructible_v, "valueless_by_exception error"); v_t v; VERIFY(!v.valueless_by_exception()); v = 42; VERIFY(!v.valueless_by_exception()); } { using v_t = variant; static_assert(eastl::is_default_constructible_v, "valueless_by_exception error"); v_t v1, v2; VERIFY(!v1.valueless_by_exception()); VERIFY(!v2.valueless_by_exception()); v1 = 42; VERIFY(!v1.valueless_by_exception()); VERIFY(!v2.valueless_by_exception()); eastl::swap(v1, v2); VERIFY(!v1.valueless_by_exception()); VERIFY(!v2.valueless_by_exception()); v1 = v2; VERIFY(!v1.valueless_by_exception()); VERIFY(!v2.valueless_by_exception()); } { struct NotDefaultConstructibleButHasConversionCtor { NotDefaultConstructibleButHasConversionCtor() = delete; NotDefaultConstructibleButHasConversionCtor(int) {} }; static_assert(!eastl::is_default_constructible::value, "valueless_by_exception error"); using v_t = variant; v_t v(42); static_assert(!eastl::is_default_constructible_v, "valueless_by_exception error"); VERIFY(!v.valueless_by_exception()); } // TODO(rparolin): review exception safety for variant types // // { // #if EASTL_EXCEPTIONS_ENABLED // struct DefaultConstructibleButThrows // { // DefaultConstructibleButThrows() {} // ~DefaultConstructibleButThrows() {} // // DefaultConstructibleButThrows(DefaultConstructibleButThrows&&) { throw 42; } // DefaultConstructibleButThrows(const DefaultConstructibleButThrows&) { throw 42; } // DefaultConstructibleButThrows& operator=(const DefaultConstructibleButThrows&) { throw 42; } // DefaultConstructibleButThrows& operator=(DefaultConstructibleButThrows&&) { throw 42; } // }; // // using v_t = variant; // // v_t v1; // VERIFY(!v1.valueless_by_exception()); // // try // { // v1 = DefaultConstructibleButThrows(); // } // catch (...) // { // VERIFY(v1.valueless_by_exception()); // } // #endif // } } return nErrorCount; } int TestVariantCopyAndMove() { using namespace eastl; int nErrorCount = 0; { { using v_t = variant; v_t v1 = 42; v_t v2 = v1; VERIFY(get(v2) == get(v1)); } } return nErrorCount; } int TestVariantEmplace() { using namespace eastl; int nErrorCount = 0; { variant v; v.emplace(42); VERIFY(get(v) == 42); } { variant v; v.emplace<0>(42); VERIFY(get<0>(v) == 42); } { variant v; v.emplace<0>(42); VERIFY(get<0>(v) == 42); v.emplace<1>(short(43)); VERIFY(get<1>(v) == short(43)); v.emplace<2>(44L); VERIFY(get<2>(v) == 44L); } { variant v; v.emplace(42); VERIFY(get(v) == 42); v.emplace(short(43)); VERIFY(get(v) == short(43)); v.emplace(44L); VERIFY(get(v) == 44L); } { { variant v; v.emplace<0>(1337); VERIFY(get<0>(v).mX == 1337); } VERIFY(TestObject::IsClear()); TestObject::Reset(); } { { variant v; v.emplace(42); VERIFY(get(v) == 42); v.emplace(1337); VERIFY(get(v).mX == 1337); v.emplace(1338, 42, 3); VERIFY(get(v).mX == 1338 + 42 + 3); } VERIFY(TestObject::IsClear()); TestObject::Reset(); } { { struct r { r() = default; r(int x) : mX(x) {} int mX; }; variant v; v.emplace<0>(42); VERIFY(get<0>(v) == 42); v.emplace<1>(1337); VERIFY(get<1>(v).mX == 1337); } } { struct r { r() = default; r(int a, int b, int c, int d) : a(a), b(b), c(c), d(d) {} r(std::initializer_list l) { auto it = l.begin(); a = *it++; b = *it++; c = *it++; d = *it++; } int a, b, c, d; }; r aa{1,2,3,4}; VERIFY(aa.a == 1); VERIFY(aa.b == 2); VERIFY(aa.c == 3); VERIFY(aa.d == 4); variant v; v.emplace<0>(std::initializer_list{1,2,3,4}); VERIFY(get(v).a == 1); VERIFY(get(v).b == 2); VERIFY(get(v).c == 3); VERIFY(get(v).d == 4); } return nErrorCount; } int TestVariantSwap() { using namespace eastl; int nErrorCount = 0; { variant v1 = 42; variant v2 = 24; v1.swap(v2); VERIFY(get(v1) == 24); VERIFY(get(v2) == 42); v1.swap(v2); VERIFY(get(v1) == 42); VERIFY(get(v2) == 24); } { variant v1 = "Hello"; variant v2 = "World"; VERIFY(get(v1) == "Hello"); VERIFY(get(v2) == "World"); v1.swap(v2); VERIFY(get(v1) == "World"); VERIFY(get(v2) == "Hello"); } return nErrorCount; } int TestVariantRelOps() { using namespace eastl; int nErrorCount = 0; { variant v1 = 42; variant v2 = 24; variant v1e = v1; VERIFY(v1 == v1e); VERIFY(v1 != v2); VERIFY(v1 > v2); VERIFY(v2 < v1); } { vector> v = {{1}, {3}, {7}, {4}, {0}, {5}, {2}, {6}, {8}}; eastl::sort(v.begin(), v.end()); VERIFY(eastl::is_sorted(v.begin(), v.end())); } return nErrorCount; } int TestVariantInplaceCtors() { using namespace eastl; int nErrorCount = 0; { variant v(in_place<0>, 42); VERIFY(get<0>(v) == 42); VERIFY(v.index() == 0); } { variant v(in_place<1>, 42); VERIFY(get<1>(v) == 42); VERIFY(v.index() == 1); } { variant v(in_place, 42); VERIFY(get<0>(v) == 42); VERIFY(v.index() == 0); } { variant v(in_place, "hello"); VERIFY(get<1>(v) == "hello"); VERIFY(v.index() == 1); } return nErrorCount; } int TestVariantVisitor() { using namespace eastl; int nErrorCount = 0; using v_t = variant; // TODO(rparolin): When we have a C++17 compiler // // template deduction guides test // template struct overloaded : Ts... { using Ts::operator()...; }; // template overloaded(Ts...) -> overloaded; // { // v_t arr[] = {42, "rob", 42.0, 42L}; // int count = 0; // for (auto& e : arr) // { // eastl::visit(overloaded{[&](int) { count++; }, // [&](string) { count++; }, // [&](double) { count++; }, // [&](long) { count++; }}, e); // } // } { v_t arr[] = {42, "hello", 42.0, 42L}; int count = 0; for (auto& e : arr) { eastl::visit([&](auto){ count++; }, e); } VERIFY(count == EAArrayCount(arr)); } { static bool bVisited = false; variant v = 42; struct MyVisitor { MyVisitor& operator()(int) { bVisited = true; return *this; }; MyVisitor& operator()(long) { return *this; }; MyVisitor& operator()(string) { return *this; }; MyVisitor& operator()(unsigned) { return *this; }; // not in variant }; visit(MyVisitor{}, v); VERIFY(bVisited); } { static bool bVisited = false; variant i = 42; variant s = "hello"; struct MultipleVisitor { MultipleVisitor& operator()(int, int) { return *this; } MultipleVisitor& operator()(int, string) { bVisited = true; return *this; } MultipleVisitor& operator()(string, int) { return *this; } MultipleVisitor& operator()(string, string) { return *this; } }; visit(MultipleVisitor{}, i, s); VERIFY(bVisited); } { variant v = 42; struct ModifyingVisitor { void operator()(int &i) { i += 1; } void operator()(string &s) { s += "hello"; } }; visit(ModifyingVisitor{}, v); VERIFY(get<0>(v) == 43); } { variant v = 42; struct ReturningVisitor { int operator()(int i) {return i;} int operator()(string s) {return 0;} }; VERIFY(visit(ReturningVisitor{}, v) == 42); } #if !defined(EA_COMPILER_MSVC) { variant v = 42; struct ReturningDifferentTypesVisitor { int operator()(int i) {return i;} size_t operator()(string s) {return s.size();} }; VERIFY(visit(ReturningDifferentTypesVisitor{}, v) == 42); } #endif return nErrorCount; } int TestVariantAssignment() { using namespace eastl; int nErrorCount = 0; { variant v = TestObject(1337); VERIFY(get(v).mX == 1337); TestObject::Reset(); v.operator=(42); // ensure assignment-operator is called VERIFY(TestObject::sTODtorCount == 1); // verify TestObject dtor is called. VERIFY(get(v) == 42); TestObject::Reset(); } return nErrorCount; } int TestVariantMoveOnly() { using namespace eastl; int nErrorCount = 0; { variant v = MoveOnlyType(1337); VERIFY(get(v).mVal == 1337); } return nErrorCount; } //compilation test related to PR #315: converting constructor and assignment operator compilation error void TestCompilation(const double e) { eastl::variant v{e}; } int TestVariantUserRegressionCopyMoveAssignmentOperatorLeak() { using namespace eastl; int nErrorCount = 0; { { eastl::variant v = TestObject(1337); VERIFY(eastl::get(v).mX == 1337); eastl::variant v2 = TestObject(1338); VERIFY(eastl::get(v2).mX == 1338); v.operator=(v2); VERIFY(eastl::get(v).mX == 1338); VERIFY(eastl::get(v2).mX == 1338); } VERIFY(TestObject::IsClear()); TestObject::Reset(); } { { eastl::variant v = TestObject(1337); VERIFY(eastl::get(v).mX == 1337); eastl::variant v2 = TestObject(1338); VERIFY(eastl::get(v2).mX == 1338); v.operator=(eastl::move(v2)); VERIFY(eastl::get(v).mX == 1338); } VERIFY(TestObject::IsClear()); TestObject::Reset(); } { { eastl::variant v = TestObject(1337); VERIFY(eastl::get(v).mX == 1337); v = {}; VERIFY(eastl::get(v).mX == 0); } VERIFY(TestObject::IsClear()); TestObject::Reset(); } return nErrorCount; } int TestVariantUserRegressionIncompleteType() { using namespace eastl; int nErrorCount = 0; { struct B; struct A { vector> v; }; struct B { vector> v; }; } return nErrorCount; } int TestVariantGeneratingComparisonOverloads(); int TestVariant() { int nErrorCount = 0; nErrorCount += TestVariantBasic(); nErrorCount += TestVariantSize(); nErrorCount += TestVariantAlternative(); nErrorCount += TestVariantValuelessByException(); nErrorCount += TestVariantGet(); nErrorCount += TestVariantHoldsAlternative(); nErrorCount += TestVariantHash(); nErrorCount += TestVariantCopyAndMove(); nErrorCount += TestVariantSwap(); nErrorCount += TestVariantEmplace(); nErrorCount += TestVariantRelOps(); nErrorCount += TestVariantInplaceCtors(); nErrorCount += TestVariantVisitor(); nErrorCount += TestVariantAssignment(); nErrorCount += TestVariantMoveOnly(); nErrorCount += TestVariantUserRegressionCopyMoveAssignmentOperatorLeak(); nErrorCount += TestVariantUserRegressionIncompleteType(); nErrorCount += TestVariantGeneratingComparisonOverloads(); return nErrorCount; } #else int TestVariant() { return 0; } #endif