///////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ///////////////////////////////////////////////////////////////////////////// #include "EASTLTest.h" #include #include #include #include #include // SmallTestObject // struct SmallTestObject { static int mCtorCount; SmallTestObject() EA_NOEXCEPT { mCtorCount++; } SmallTestObject(const SmallTestObject&) EA_NOEXCEPT { mCtorCount++; } SmallTestObject(SmallTestObject&&) EA_NOEXCEPT { mCtorCount++; } SmallTestObject& operator=(const SmallTestObject&) EA_NOEXCEPT { mCtorCount++; return *this; } ~SmallTestObject() EA_NOEXCEPT { mCtorCount--; } static void Reset() { mCtorCount = 0; } static bool IsClear() { return mCtorCount == 0; } }; int SmallTestObject::mCtorCount = 0; // RequiresInitList // struct RequiresInitList { RequiresInitList(std::initializer_list ilist) : sum(eastl::accumulate(begin(ilist), end(ilist), 0)) {} int sum; }; int TestAny() { using namespace eastl; int nErrorCount = 0; // NOTE(rparolin): Ensure 'any' is at least the size of an eastl::string and an eastl::vector to prevent heap // allocation of handle objects (objects that point to a heap allocation). This will reduce memory pressure since // eastl::string will be a commonly used type. We could also test with a vector. { static_assert(sizeof(eastl::string) <= sizeof(eastl::any), "ensure that 'any' has enough local memory to store a string"); static_assert(sizeof(eastl::vector) <= sizeof(eastl::any), "ensure that 'any' has enough local memory to store a vector"); } { // default construct any a; VERIFY(a.has_value() == false); } { // test object ctors & dtors are called for a large object TestObject::Reset(); { any a{TestObject()}; } VERIFY(TestObject::IsClear()); } { // test object ctors & dtors are called for a small object SmallTestObject::Reset(); { any a{SmallTestObject()}; } VERIFY(SmallTestObject::IsClear()); } { any a(42); VERIFY(a.has_value() == true); VERIFY(any_cast(a) == 42); VERIFY(any_cast(a) != 1337); any_cast(a) = 10; VERIFY(any_cast(a) == 10); a = 1.f; any_cast(a) = 1337.f; VERIFY(any_cast(a) == 1337.f); a = 4343; VERIFY(any_cast(a) == 4343); a = string("hello world"); VERIFY(any_cast(a) == "hello world"); VERIFY(any_cast(a) == "hello world"); } { struct custom_type { int data; }; any a = custom_type{}; any_cast(a).data = 42; VERIFY(any_cast(a).data == 42); } { any a = 42; VERIFY(any_cast(a) == 42); #if EASTL_EXCEPTIONS_ENABLED int throwCount = 0; try { VERIFY(any_cast(a) == 42); } catch (bad_any_cast) { throwCount++; } VERIFY(throwCount != 0); #endif } { vector va = {42, 'a', 42.f, 3333u, 4444ul, 5555ull, 6666.0}; VERIFY(any_cast(va[0]) == 42); VERIFY(any_cast(va[1]) == 'a'); VERIFY(any_cast(va[2]) == 42.f); VERIFY(any_cast(va[3]) == 3333u); VERIFY(any_cast(va[4]) == 4444ul); VERIFY(any_cast(va[5]) == 5555ull); VERIFY(any_cast(va[6]) == 6666.0); } { any a(string("test string")); VERIFY(a.has_value()); VERIFY(any_cast(a) == "test string"); } { vector va = {42, string("rob"), 'a', 42.f}; VERIFY(any_cast(va[0]) == 42); VERIFY(any_cast(va[1]) == "rob"); VERIFY(any_cast(va[2]) == 'a'); VERIFY(any_cast(va[3]) == 42.f); } { vector va; va.push_back(42); va.push_back(string("rob")); va.push_back('a'); va.push_back(42.f); VERIFY(any_cast(va[0]) == 42); VERIFY(any_cast(va[1]) == "rob"); VERIFY(any_cast(va[2]) == 'a'); VERIFY(any_cast(va[3]) == 42.f); } // NOTE(rparolin): Replaces a small 'any' object with a large one and make sure it doesn't corrupt // the surrounding memory in the vector. { TestObject::Reset(); { vector va = {42, 'a', 42.f, 3333u, 4444ul, 5555ull, 6666.0}; VERIFY(any_cast(va[0]) == 42); VERIFY(any_cast(va[1]) == 'a'); VERIFY(any_cast(va[2]) == 42.f); VERIFY(any_cast(va[3]) == 3333u); VERIFY(any_cast(va[4]) == 4444ul); VERIFY(any_cast(va[5]) == 5555ull); VERIFY(any_cast(va[6]) == 6666.0); va[3] = TestObject(3333); // replace a small integral with a large heap allocated object. VERIFY(any_cast(va[0]) == 42); VERIFY(any_cast(va[1]) == 'a'); VERIFY(any_cast(va[2]) == 42.f); VERIFY(any_cast(va[3]).mX == 3333); // not 3333u because TestObject ctor takes a signed type. VERIFY(any_cast(va[4]) == 4444ul); VERIFY(any_cast(va[5]) == 5555ull); VERIFY(any_cast(va[6]) == 6666.0); } VERIFY(TestObject::IsClear()); } { any a(string("test string")); VERIFY(a.has_value()); a.reset(); VERIFY(!a.has_value()); } { any a1 = 42; any a2 = a1; VERIFY(a1.has_value()); VERIFY(a2.has_value()); VERIFY(any_cast(a1) == any_cast(a2)); } { any a1; VERIFY(!a1.has_value()); { any a2(string("test string")); a1 = any_cast(a2); VERIFY(a1.has_value()); } VERIFY(any_cast(a1) == "test string"); VERIFY(a1.has_value()); } { any a1; VERIFY(!a1.has_value()); { any a2(string("test string")); a1 = a2; VERIFY(a1.has_value()); } VERIFY(any_cast(a1) == "test string"); VERIFY(a1.has_value()); } // swap tests { { any a1 = 42; any a2 = 24; VERIFY(any_cast(a1) == 42); VERIFY(any_cast(a2) == 24); a1.swap(a2); VERIFY(any_cast(a1) == 24); VERIFY(any_cast(a2) == 42); eastl::swap(a1, a2); VERIFY(any_cast(a1) == 42); VERIFY(any_cast(a2) == 24); } { any a1 = string("hello"); any a2 = string("world"); VERIFY(any_cast(a1) == "hello"); VERIFY(any_cast(a2) == "world"); a1.swap(a2); VERIFY(any_cast(a1) == "world"); VERIFY(any_cast(a2) == "hello"); eastl::swap(a1, a2); VERIFY(any_cast(a1) == "hello"); VERIFY(any_cast(a2) == "world"); } } #if EASTL_RTTI_ENABLED { #if defined(EA_COMPILER_MSVC) VERIFY(EA::StdC::Strcmp(any(42).type().name(), "int") == 0); VERIFY(EA::StdC::Strcmp(any(42.f).type().name(), "float") == 0); VERIFY(EA::StdC::Strcmp(any(42u).type().name(), "unsigned int") == 0); VERIFY(EA::StdC::Strcmp(any(42ul).type().name(), "unsigned long") == 0); VERIFY(EA::StdC::Strcmp(any(42l).type().name(), "long") == 0); #elif defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC) VERIFY(EA::StdC::Strcmp(any(42).type().name(), "i") == 0); VERIFY(EA::StdC::Strcmp(any(42.f).type().name(), "f") == 0); VERIFY(EA::StdC::Strcmp(any(42u).type().name(), "j") == 0); VERIFY(EA::StdC::Strcmp(any(42ul).type().name(), "m") == 0); VERIFY(EA::StdC::Strcmp(any(42l).type().name(), "l") == 0); #endif } #endif // emplace, small object tests { any a; a.emplace(42); VERIFY(a.has_value()); VERIFY(any_cast(a) == 42); a.emplace((short)8); // no way to define a short literal we must cast here. VERIFY(any_cast(a) == 8); VERIFY(a.has_value()); a.reset(); VERIFY(!a.has_value()); } // emplace, large object tests { TestObject::Reset(); { any a; a.emplace(); VERIFY(a.has_value()); } VERIFY(TestObject::IsClear()); } // emplace, initializer_list { { any a; a.emplace(std::initializer_list{1,2,3,4,5,6}); VERIFY(a.has_value()); VERIFY(any_cast(a).sum == 21); } } // equivalence tests { any a, b; VERIFY(!a.has_value() == !b.has_value()); #if EASTL_EXCEPTIONS_ENABLED int bad_any_cast_thrown = 0; try { VERIFY(any_cast(a) == any_cast(b)); } catch (eastl::bad_any_cast) { bad_any_cast_thrown++; } VERIFY(bad_any_cast_thrown != 0); #endif a = 42; b = 24; VERIFY(any_cast(a) != any_cast(b)); VERIFY(a.has_value() == b.has_value()); a = 42; b = 42; VERIFY(any_cast(a) == any_cast(b)); VERIFY(a.has_value() == b.has_value()); } // move tests { any a = string("hello world"); VERIFY(any_cast(a) == "hello world"); auto s = move(any_cast(a)); // move string out VERIFY(s == "hello world"); VERIFY(any_cast(a).empty()); any_cast(a) = move(s); // move string in VERIFY(any_cast(a) == "hello world"); } // nullptr tests { any* a = nullptr; VERIFY(any_cast(a) == nullptr); VERIFY(any_cast(a) == nullptr); VERIFY(any_cast(a) == nullptr); VERIFY(any_cast(a) == nullptr); any b; VERIFY(any_cast(&b) == nullptr); VERIFY(any_cast(&b) == nullptr); VERIFY(any_cast(&b) == nullptr); VERIFY(any_cast(&b) == nullptr); VERIFY(any_cast(&b) == nullptr); VERIFY(any_cast(&b) == nullptr); VERIFY(any_cast(&b) == nullptr); VERIFY(any_cast(&b) == nullptr); } // Aligned type tests { { any a = Align16(1337); VERIFY(any_cast(a) == Align16(1337)); } { any a = Align32(1337); VERIFY(any_cast(a) == Align32(1337)); } { any a = Align64(1337); VERIFY(any_cast(a) == Align64(1337)); } } // make_any { { auto a = make_any(42); VERIFY(any_cast(a) == 42); } { auto a = make_any(std::initializer_list{1,2,3,4,5,6,7,8}); VERIFY(any_cast(a).sum == 36); } } // user reported regression that eastl::any constructor was not decaying the deduced type correctly. { float f = 42.f; eastl::any a(f); VERIFY(any_cast(a) == 42.f); } //testing unsafe operations { eastl::any a = 1; int* i = eastl::any_cast(&a); VERIFY((*i) == 1); a = 2; int *j = (int*)eastl::unsafe_any_cast(&a); VERIFY((*j) == 2); const eastl::any b = 3; const void * p = eastl::unsafe_any_cast(&b); void *q = const_cast(p); int *r = static_cast(q); VERIFY((*r) == 3); } // user regression when calling the assignment operator { { eastl::any a1; eastl::any a2; VERIFY(a1.has_value() == false); VERIFY(a2.has_value() == false); a1 = a2; VERIFY(a1.has_value() == false); VERIFY(a2.has_value() == false); } { eastl::any a1 = 42; eastl::any a2; VERIFY(a1.has_value() == true); VERIFY(a2.has_value() == false); a1 = a2; VERIFY(a1.has_value() == false); VERIFY(a2.has_value() == false); } { eastl::any a1; eastl::any a2 = 42; VERIFY(a1.has_value() == false); VERIFY(a2.has_value() == true); a1 = a2; VERIFY(a1.has_value() == true); VERIFY(a2.has_value() == true); VERIFY(any_cast(a1) == 42); VERIFY(any_cast(a2) == 42); } } return nErrorCount; }