///////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ///////////////////////////////////////////////////////////////////////////// #include "EASTLTest.h" #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////// struct IntStruct { IntStruct(int in) : data(in) {} int data; }; bool operator<(const IntStruct& lhs, const IntStruct& rhs) { return lhs.data < rhs.data; } bool operator==(const IntStruct& lhs, const IntStruct& rhs) { return lhs.data == rhs.data; } ///////////////////////////////////////////////////////////////////////////// struct destructor_test { ~destructor_test() { destructor_ran = true; } static bool destructor_ran; static void reset() { destructor_ran = false; } }; bool destructor_test::destructor_ran = false; ///////////////////////////////////////////////////////////////////////////// struct move_test { move_test() = default; move_test(move_test&&) { was_moved = true; } move_test& operator=(move_test&&) { was_moved = true; return *this;} // issue a compiler error is container tries to copy move_test(move_test const&) = delete; move_test& operator=(const move_test&) = delete; static bool was_moved; }; bool move_test::was_moved = false; ///////////////////////////////////////////////////////////////////////////// template class forwarding_test { eastl::optional m_optional; public: forwarding_test() : m_optional() {} forwarding_test(T&& t) : m_optional(t) {} ~forwarding_test() { m_optional.reset(); } template T GetValueOrDefault(U&& def) const { return m_optional.value_or(eastl::forward(def)); } }; ///////////////////////////////////////////////////////////////////////////// struct assignment_test { assignment_test() { ++num_objects_inited; } assignment_test(assignment_test&&) { ++num_objects_inited; } assignment_test(const assignment_test&) { ++num_objects_inited; } assignment_test& operator=(assignment_test&&) { return *this; } assignment_test& operator=(const assignment_test&) { return *this; } ~assignment_test() { --num_objects_inited; } static int num_objects_inited; }; int assignment_test::num_objects_inited = 0; ///////////////////////////////////////////////////////////////////////////// // TestOptional // int TestOptional() { using namespace eastl; int nErrorCount = 0; #if defined(EASTL_OPTIONAL_ENABLED) && EASTL_OPTIONAL_ENABLED { { VERIFY( (is_same::value_type, int>::value)); VERIFY( (is_same::value_type, short>::value)); VERIFY(!(is_same::value_type, long>::value)); VERIFY( (is_same::value_type, const short>::value)); VERIFY( (is_same::value_type, volatile short>::value)); VERIFY( (is_same::value_type, const volatile short>::value)); VERIFY(is_empty::value); #if EASTL_TYPE_TRAIT_is_literal_type_CONFORMANCE VERIFY(is_literal_type::value); #endif #if EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE VERIFY(is_trivially_destructible::value); VERIFY(is_trivially_destructible>::value); VERIFY(is_trivially_destructible>::value); VERIFY(is_trivially_destructible>::value == is_trivially_destructible::value); #endif { struct NotTrivialDestructible { ~NotTrivialDestructible() {} }; VERIFY(!is_trivially_destructible::value); VERIFY(!is_trivially_destructible>::value); VERIFY(!is_trivially_destructible>::value); VERIFY(is_trivially_destructible>::value == is_trivially_destructible::value); } } { optional o; VERIFY(!o); VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D); o = 1024; VERIFY(static_cast(o)); VERIFY(o.value_or(0x8BADF00D) == 1024); VERIFY(o.value() == 1024); // Test reset o.reset(); VERIFY(!o); VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D); } { optional o(nullopt); VERIFY(!o); VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D); } { optional o = {}; VERIFY(!o); VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D); } { optional o(42); VERIFY(bool(o)); VERIFY(o.value_or(0x8BADF00D) == 42); o = nullopt; VERIFY(!o); VERIFY(o.value_or(0x8BADF00D) == (int)0x8BADF00D); } { optional o(42); VERIFY(static_cast(o)); VERIFY(o.value_or(0x8BADF00D) == 42); VERIFY(o.value() == 42); } { auto o = make_optional(42); VERIFY((is_same>::value)); VERIFY(static_cast(o)); VERIFY(o.value_or(0x8BADF00D) == 42); VERIFY(o.value() == 42); } { int a = 42; auto o = make_optional(a); VERIFY((is_same::value)); VERIFY(o.value() == 42); } { // test make_optional stripping refs/cv-qualifers int a = 42; const volatile int& intRef = a; auto o = make_optional(intRef); VERIFY((is_same::value)); VERIFY(o.value() == 42); } { int a = 10; const volatile int& aRef = a; auto o = eastl::make_optional(aRef); VERIFY(o.value() == 10); } { { struct local { int payload1; }; auto o = eastl::make_optional(42); VERIFY(o.value().payload1 == 42); } { struct local { int payload1; int payload2; }; auto o = eastl::make_optional(42, 43); VERIFY(o.value().payload1 == 42); VERIFY(o.value().payload2 == 43); } { struct local { local(std::initializer_list ilist) { payload1 = ilist.begin()[0]; payload2 = ilist.begin()[1]; } int payload1; int payload2; }; auto o = eastl::make_optional({42, 43}); VERIFY(o.value().payload1 == 42); VERIFY(o.value().payload2 == 43); } } { optional o1(42), o2(24); VERIFY(o1.value() == 42); VERIFY(o2.value() == 24); VERIFY(*o1 == 42); VERIFY(*o2 == 24); o1 = eastl::move(o2); VERIFY(*o2 == 24); VERIFY(*o1 == 24); VERIFY(o2.value() == 24); VERIFY(o1.value() == 24); VERIFY(bool(o1)); VERIFY(bool(o2)); } { struct local { int payload; }; optional o = local{ 42 }; VERIFY(o->payload == 42); } { struct local { int test() const { return 42; } }; { const optional o = local{}; VERIFY(o->test() == 42); VERIFY((*o).test() == 42); VERIFY(o.value().test() == 42); VERIFY(bool(o)); } { optional o = local{}; VERIFY(bool(o)); o = nullopt; VERIFY(!bool(o)); VERIFY(o.value_or(local{}).test() == 42); VERIFY(!bool(o)); } } } { move_test t; optional o(eastl::move(t)); VERIFY(move_test::was_moved); } { forwarding_testft(1.f); float val = ft.GetValueOrDefault(0.f); VERIFY(val == 1.f); } { assignment_test::num_objects_inited = 0; { optional o1; optional o2 = assignment_test(); optional o3(o2); VERIFY(assignment_test::num_objects_inited == 2); o1 = nullopt; VERIFY(assignment_test::num_objects_inited == 2); o1 = o2; VERIFY(assignment_test::num_objects_inited == 3); o1 = o2; VERIFY(assignment_test::num_objects_inited == 3); o1 = nullopt; VERIFY(assignment_test::num_objects_inited == 2); o2 = o1; VERIFY(assignment_test::num_objects_inited == 1); o1 = o2; VERIFY(assignment_test::num_objects_inited == 1); } VERIFY(assignment_test::num_objects_inited == 0); { optional o1; VERIFY(assignment_test::num_objects_inited == 0); o1 = nullopt; VERIFY(assignment_test::num_objects_inited == 0); o1 = optional(assignment_test()); VERIFY(assignment_test::num_objects_inited == 1); o1 = optional(assignment_test()); VERIFY(assignment_test::num_objects_inited == 1); optional o2(eastl::move(o1)); VERIFY(assignment_test::num_objects_inited == 2); o1 = nullopt; VERIFY(assignment_test::num_objects_inited == 1); } VERIFY(assignment_test::num_objects_inited == 0); } #if EASTL_VARIADIC_TEMPLATES_ENABLED { struct vec3 { vec3(std::initializer_list ilist) { auto* p = ilist.begin(); x = *p++; y = *p++; z = *p++; } vec3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} // testing variadic template constructor overload float x = 0, y = 0, z = 0; }; { optional o{ in_place, 4.f, 5.f, 6.f }; VERIFY(o->x == 4 && o->y == 5 && o->z == 6); } { optional o{ in_place, {4.f, 5.f, 6.f} }; VERIFY(o->x == 4 && o->y == 5 && o->z == 6); } { optional o(in_place, {'a', 'b', 'c'}); VERIFY(o == string("abc")); } // http://en.cppreference.com/w/cpp/utility/optional/emplace { optional o; o.emplace(42.f, 42.f, 42.f); VERIFY(o->x == 42.f && o->y == 42.f && o->z == 42.f); } { optional o; o.emplace({42.f, 42.f, 42.f}); VERIFY(o->x == 42.f && o->y == 42.f && o->z == 42.f); } { optional o; o.emplace(42); VERIFY(*o == 42); } struct nonCopyableNonMovable { nonCopyableNonMovable(int v) : val(v) {} nonCopyableNonMovable(const nonCopyableNonMovable&) = delete; nonCopyableNonMovable(nonCopyableNonMovable&&) = delete; nonCopyableNonMovable& operator=(const nonCopyableNonMovable&) = delete; int val = 0; }; { optional o; o.emplace(42); VERIFY(o->val == 42); } { // Verify emplace will destruct object if it has been engaged. destructor_test::reset(); optional o; o.emplace(); VERIFY(!destructor_test::destructor_ran); destructor_test::reset(); o.emplace(); VERIFY(destructor_test::destructor_ran); } } #endif // swap { { optional o1 = 42, o2 = 24; VERIFY(*o1 == 42); VERIFY(*o2 == 24); o1.swap(o2); VERIFY(*o1 == 24); VERIFY(*o2 == 42); } { optional o1 = 42, o2 = 24; VERIFY(*o1 == 42); VERIFY(*o2 == 24); swap(o1, o2); VERIFY(*o1 == 24); VERIFY(*o2 == 42); } { optional o1 = 42, o2; VERIFY(*o1 == 42); VERIFY(o2.has_value() == false); swap(o1, o2); VERIFY(o1.has_value() == false); VERIFY(*o2 == 42); } { optional o1 = nullopt, o2 = 42; VERIFY(o1.has_value() == false); VERIFY(*o2 == 42); swap(o1, o2); VERIFY(*o1 == 42); VERIFY(o2.has_value() == false); } } { optional o(in_place, 10); optional e; VERIFY(o < IntStruct(42)); VERIFY(!(o < IntStruct(2))); VERIFY(!(o < IntStruct(10))); VERIFY(e < o); VERIFY(e < IntStruct(10)); VERIFY(o > IntStruct(4)); VERIFY(!(o > IntStruct(42))); VERIFY(o >= IntStruct(4)); VERIFY(o >= IntStruct(10)); VERIFY(IntStruct(4) <= o); VERIFY(IntStruct(10) <= o); VERIFY(o == IntStruct(10)); VERIFY(o->data == IntStruct(10).data); VERIFY(o != IntStruct(11)); VERIFY(o->data != IntStruct(11).data); VERIFY(e == nullopt); VERIFY(nullopt == e); VERIFY(o != nullopt); VERIFY(nullopt != o); VERIFY(nullopt < o); VERIFY(o > nullopt); VERIFY(!(nullopt > o)); VERIFY(!(o < nullopt)); VERIFY(nullopt <= o); VERIFY(o >= nullopt); } // hash { { // verify that the hash an empty eastl::optional object is zero. typedef hash> hash_optional_t; optional e; VERIFY(hash_optional_t{}(e) == 0); } { // verify that the hash is the same as the hash of the underlying type const char* const pMessage = "Electronic Arts Canada"; typedef hash> hash_optional_t; optional o = string(pMessage); VERIFY(hash_optional_t{}(o) == hash{}(pMessage)); } } // sorting { vector> v = {{122}, {115}, nullopt, {223}}; sort(begin(v), end(v)); vector> sorted = {nullopt, 115, 122, 223}; VERIFY(v == sorted); } // test destructors being called. { destructor_test::reset(); { optional o = destructor_test{}; } VERIFY(destructor_test::destructor_ran); destructor_test::reset(); { optional o; } // destructor shouldn't be called as object wasn't constructed. VERIFY(!destructor_test::destructor_ran); destructor_test::reset(); { optional o = {}; } // destructor shouldn't be called as object wasn't constructed. VERIFY(!destructor_test::destructor_ran); destructor_test::reset(); { optional o = nullopt; } // destructor shouldn't be called as object wasn't constructed. VERIFY(!destructor_test::destructor_ran); } // optional rvalue tests { VERIFY(*optional(1) == 1); VERIFY( optional(1).value() == 1); VERIFY( optional(1).value_or(0xdeadf00d) == 1); VERIFY( optional().value_or(0xdeadf00d) == 0xdeadf00d); VERIFY( optional(1).has_value() == true); VERIFY( optional().has_value() == false); VERIFY( optional(in_place, 10)->data == 10); } // alignment type tests { static_assert(alignof(optional) == alignof(Align16), "optional alignment failure"); static_assert(alignof(optional) == alignof(Align32), "optional alignment failure"); static_assert(alignof(optional) == alignof(Align64), "optional alignment failure"); } { // user reported regression that failed to compile struct local_struct { local_struct() {} ~local_struct() {} }; static_assert(!eastl::is_trivially_destructible_v, ""); { local_struct ls; eastl::optional o{ls}; } { const local_struct ls; eastl::optional o{ls}; } } { { // user regression eastl::optional o = eastl::string("Hello World"); eastl::optional co; co = o; // force copy-assignment VERIFY( o.value().data() != co.value().data()); VERIFY( o.value().data() == eastl::string("Hello World")); VERIFY(co.value().data() == eastl::string("Hello World")); } { // user regression EA_DISABLE_VC_WARNING(4625 4626) // copy/assignment operator constructor was implicitly defined as deleted struct local { eastl::unique_ptr ptr; }; EA_RESTORE_VC_WARNING() eastl::optional o1 = local{eastl::make_unique(42)}; eastl::optional o2; o2 = eastl::move(o1); VERIFY(!!o1 == true); VERIFY(!!o2 == true); VERIFY(!!o1->ptr == false); VERIFY(!!o2->ptr == true); VERIFY(o2->ptr.get() != nullptr); } { // user regression static bool copyCtorCalledWithUninitializedValue; static bool moveCtorCalledWithUninitializedValue; copyCtorCalledWithUninitializedValue = moveCtorCalledWithUninitializedValue = false; struct local { int val; local() : val(0xabcdabcd) {} local(const local& other) : val(other.val) { if (other.val != 0xabcdabcd) copyCtorCalledWithUninitializedValue = true; } local(local&& other) : val(eastl::move(other.val)) { if (other.val != 0xabcdabcd) moveCtorCalledWithUninitializedValue = true; } local& operator=(const local&) = delete; }; eastl::optional n; eastl::optional o1(n); VERIFY(!copyCtorCalledWithUninitializedValue); eastl::optional o2(eastl::move(n)); VERIFY(!moveCtorCalledWithUninitializedValue); } } { auto testFn = []() -> optional { return eastl::nullopt; }; auto o = testFn(); VERIFY(!!o == false); } #endif // EASTL_OPTIONAL_ENABLED return nErrorCount; }