///////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ///////////////////////////////////////////////////////////////////////////// #include #include "EASTLTest.h" #include "GetTypeName.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include EA_DISABLE_ALL_VC_WARNINGS() #include #include #ifdef EA_PLATFORM_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #elif defined(EA_PLATFORM_ANDROID) #include #endif EA_RESTORE_ALL_VC_WARNINGS() EA_DISABLE_VC_WARNING(4702 4800) // 4702: unreachable code // 4800: forcing value to bool 'true' or 'false' namespace SmartPtrTest { /// CustomDeleter /// /// Used for testing unique_ptr deleter overrides. Otherwise acts the same as the default deleter. /// struct CustomDeleter { template void operator()(const T* p) const // We use a const argument type in order to be most flexible with what types we accept. { delete const_cast(p); } CustomDeleter() {} CustomDeleter(const CustomDeleter&) {} CustomDeleter(CustomDeleter&&) {} CustomDeleter& operator=(const CustomDeleter&) { return *this; } CustomDeleter& operator=(CustomDeleter&&) { return *this; } }; struct CustomArrayDeleter { template void operator()(const T* p) const // We use a const argument type in order to be most flexible with what types we accept. { delete[] const_cast(p); } CustomArrayDeleter() {} CustomArrayDeleter(const CustomArrayDeleter&) {} CustomArrayDeleter(CustomArrayDeleter&&) {} CustomArrayDeleter& operator=(const CustomArrayDeleter&) { return *this; } CustomArrayDeleter& operator=(CustomArrayDeleter&&) { return *this; } }; /// A /// /// This is used for various tests. /// struct A { char mc; static int mCount; A(char c = 0) : mc(c) { ++mCount; } A(const A& x) : mc(x.mc) { ++mCount; } A& operator=(const A& x) { mc = x.mc; return *this; } virtual ~A() // Virtual because we subclass A below. { --mCount; } }; int A::mCount = 0; /// B /// struct B : public A { }; /// RefCountTest /// /// This is used for tests involving intrusive_ptr. /// struct RefCountTest { int mRefCount; static int mCount; RefCountTest() : mRefCount(0) { ++mCount; } RefCountTest(const RefCountTest&) : mRefCount(0) { ++mCount; } RefCountTest& operator=(const RefCountTest&) { return *this; } virtual ~RefCountTest() { --mCount; } virtual int AddRef() { return (int)((mRefCount++) + 1); } virtual int Release() { int rc = (int)((mRefCount--) - 1); if(rc) return rc; mRefCount = 1; delete this; return 0; } }; int RefCountTest::mCount = 0; /// Test /// /// This is used for tests involving intrusive_ptr. /// struct Test : public RefCountTest { bool* mpBool; Test(bool* pBool) : mpBool(pBool) { *pBool = true; } Test(const Test& x): RefCountTest(x), mpBool(x.mpBool) { } Test& operator=(const Test& x) { mpBool = x.mpBool; return *this; } ~Test() { *mpBool = false; } }; /// IntrusiveParent / IntrusiveChild /// /// This is used for tests involving intrusive_ptr. /// struct IntrusiveParent : public RefCountTest { }; struct IntrusiveChild : public IntrusiveParent { }; /// intrusive_ptr_add_ref / intrusive_ptr_release /// /// This is used for tests involving intrusive_ptr. /// struct IntrusiveCustom : public RefCountTest { static int mAddRefCallCount; static int mReleaseCallCount; virtual int AddRef() { ++mAddRefCallCount; return RefCountTest::AddRef(); } virtual int Release() { ++mReleaseCallCount; return RefCountTest::Release(); } }; int IntrusiveCustom::mAddRefCallCount = 0; int IntrusiveCustom::mReleaseCallCount = 0; void intrusive_ptr_add_ref(IntrusiveCustom* p) { p->AddRef(); } void intrusive_ptr_release(IntrusiveCustom* p) { p->Release(); } /// ParentClass / ChildClass / GrandChildClass /// /// This is used for tests involving shared_ptr. /// struct ParentClass { virtual ~ParentClass() { } virtual void DoNothingParentClass() { } }; struct ChildClass : public ParentClass { virtual void DoNothingChildClass() { } }; struct GrandChildClass : public ChildClass { virtual void DoNothingGrandChildClass() { } }; /// NamedClass /// struct NamedClass { const char* mpName; const char* mpName2; static int mnCount; NamedClass(const char* pName = NULL) : mpName(pName), mpName2(NULL) { ++mnCount; } NamedClass(const char* pName, const char* pName2) : mpName(pName), mpName2(pName2) { ++mnCount; } NamedClass(const NamedClass& x) : mpName(x.mpName), mpName2(x.mpName2) { ++mnCount; } NamedClass& operator=(const NamedClass& x) { mpName = x.mpName; mpName2 = x.mpName2; return *this; } ~NamedClass() { --mnCount; } }; int NamedClass::mnCount = 0; /// Y /// /// This is used for tests involving shared_ptr and enabled_shared_from_this. /// struct Y : public eastl::enable_shared_from_this { static int mnCount; Y() { ++mnCount; } Y(const Y&) { ++mnCount; } Y& operator=(const Y&) { return *this; } ~Y() { --mnCount; } eastl::shared_ptr f() { return shared_from_this(); } }; int Y::mnCount = 0; /// ACLS / BCLS /// /// This is used for tests involving shared_ptr. /// class ACLS : public eastl::enable_shared_from_this { public: static int mnCount; int a; ACLS(int _a_ = 0) : a(_a_) { ++mnCount; } ACLS(const ACLS& x) : a(x.a) { ++mnCount; } ACLS& operator=(const ACLS& x) { a = x.a; return *this; } ~ACLS() { --mnCount; } }; int ACLS::mnCount = 0; class BCLS : public ACLS { public: static int mnCount; int b; BCLS(int _b_ = 0) : b(_b_) { ++mnCount; } BCLS(const BCLS& x) : ACLS(x), b(x.b) { ++mnCount; } BCLS& operator=(const BCLS& x) { b = x.b; ACLS::operator=(x); return *this; } ~BCLS() { --mnCount; } }; int BCLS::mnCount = 0; /// A1 / B1 /// /// This is used for tests involving shared_ptr. /// struct A1 { static int mnCount; int a; A1(int _a_ = 0) : a(_a_) { ++mnCount; } A1(const A1& x) : a(x.a) { ++mnCount; } A1& operator=(const A1& x) { a = x.a; return *this; } ~A1() { --mnCount; } }; int A1::mnCount = 0; struct B1 : public A1 { static int mnCount; int b; B1(int _b_ = 0) : b(_b_) { ++mnCount; } B1(const B1& x) : A1(x), b(x.b) { ++mnCount; } B1& operator=(const B1& x) { b = x.b; A1::operator=(x); return *this; } ~B1() { --mnCount; } }; int B1::mnCount = 0; class MockObject { public: MockObject(bool* pAlloc) : mpAlloc(pAlloc){ *mpAlloc = true; } ~MockObject() { *mpAlloc = false; } bool IsAllocated() const { return *mpAlloc; } bool* GetAllocPtr() const { return mpAlloc; } private: bool* mpAlloc; }; class DerivedMockObject : public MockObject { public: DerivedMockObject(bool* pAlloc) : MockObject(pAlloc) {} }; struct foo : public eastl::enable_shared_from_this { foo() : mX(0){} int mX; }; struct CheckUPtrEmptyInDestructor { ~CheckUPtrEmptyInDestructor() { if(mpUPtr) mCheckUPtrEmpty = (*mpUPtr == nullptr); } eastl::unique_ptr* mpUPtr{}; static bool mCheckUPtrEmpty; }; bool CheckUPtrEmptyInDestructor::mCheckUPtrEmpty = false; struct CheckUPtrArrayEmptyInDestructor { ~CheckUPtrArrayEmptyInDestructor() { if(mpUPtr) mCheckUPtrEmpty = (*mpUPtr == nullptr); } eastl::unique_ptr* mpUPtr{}; static bool mCheckUPtrEmpty; }; bool CheckUPtrArrayEmptyInDestructor::mCheckUPtrEmpty = false; } // namespace SmartPtrTest static int Test_unique_ptr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); { EATEST_VERIFY(A::mCount == 0); // explicit unique_ptr(pointer pValue) noexcept unique_ptr pT1(new int(5)); EATEST_VERIFY(*pT1 == 5); // (reference) operator*() const *pT1 = 3; EATEST_VERIFY(*pT1 == 3); // explicit unique_ptr(pointer pValue) noexcept unique_ptr pT2(new A(1)); EATEST_VERIFY(pT2->mc == 1); EATEST_VERIFY(A::mCount == 1); // Pointers of derived types are allowed (unlike array unique_ptr) unique_ptr pT1B(new B); EATEST_VERIFY(pT1B.get() != NULL); EATEST_VERIFY(A::mCount == 2); A* pA = pT1B.release(); // release simply forgets the owned pointer. EATEST_VERIFY(pT1B.get() == NULL); EATEST_VERIFY(A::mCount == 2); delete pA; EATEST_VERIFY(A::mCount == 1); // pointer operator->() const noexcept pT2->mc = 5; EATEST_VERIFY(pT2.get()->mc == 5); // void reset(pointer pValue = pointer()) noexcept pT2.reset(new A(2)); EATEST_VERIFY(pT2->mc == 2); EATEST_VERIFY(A::mCount == 1); pT2.reset(0); EATEST_VERIFY(pT2.get() == (A*)0); EATEST_VERIFY(A::mCount == 0); pT2.reset(new A(3)); EATEST_VERIFY(pT2->mc == 3); EATEST_VERIFY(A::mCount == 1); unique_ptr pT3(new A(4)); EATEST_VERIFY(pT3->mc == 4); EATEST_VERIFY(A::mCount == 2); // void swap(this_type& scopedPtr) noexcept pT2.swap(pT3); EATEST_VERIFY(pT2->mc == 4); EATEST_VERIFY(pT3->mc == 3); EATEST_VERIFY(A::mCount == 2); // void swap(unique_ptr& scopedPtr1, unique_ptr& scopedPtr2) noexcept swap(pT2, pT3); EATEST_VERIFY(pT2->mc == 3); EATEST_VERIFY(pT3->mc == 4); EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get())); EATEST_VERIFY(A::mCount == 2); // pointer release() noexcept unique_ptr pRelease(new A); EATEST_VERIFY(A::mCount == 3); pA = pRelease.release(); delete pA; EATEST_VERIFY(A::mCount == 2); // constexpr unique_ptr() noexcept unique_ptr pT4; EATEST_VERIFY(pT4.get() == (A*)0); if(pT4) EATEST_VERIFY(pT4.get()); // Will fail if(!(!pT4)) EATEST_VERIFY(pT4.get()); // Will fail pT4.reset(new A(0)); if(!pT4) EATEST_VERIFY(!pT4.get()); // Will fail EATEST_VERIFY(A::mCount == 3); // unique_ptr(nullptr_t) noexcept unique_ptr pT5(nullptr); EATEST_VERIFY(pT5.get() == (A*)0); // unique_ptr(pointer pValue, deleter) noexcept CustomDeleter customADeleter; unique_ptr pT6(new A(17), customADeleter); EATEST_VERIFY(pT6->mc == 17); // unique_ptr(pointer pValue, typename eastl::remove_reference::type&& deleter) noexcept unique_ptr pT7(new A(18), CustomDeleter()); EATEST_VERIFY(pT7->mc == 18); // unique_ptr(this_type&& x) noexcept unique_ptr pT8(eastl::move(pT7)); EATEST_VERIFY(pT8->mc == 18); // unique_ptr(unique_ptr&& u, ...) unique_ptr > pT9(eastl::move(pT2)); // this_type& operator=(this_type&& u) noexcept // operator=(unique_ptr&& u) noexcept //unique_ptr pTVoid; //unique_ptr pTInt(new int(1)); //pTVoid.operator=(eastl::move(pTInt)); // This doesn't work because CustomDeleter doesn't know how to delete void*. Need to rework this test. // this_type& operator=(nullptr_t) noexcept pT6 = nullptr; EATEST_VERIFY(pT6.get() == (A*)0); // user reported regression // ensure a unique_ptr containing nullptr doesn't call the deleter when its destroyed. { static bool sLocalDeleterCalled; sLocalDeleterCalled = false; struct LocalDeleter { void operator()(int* p) const { sLocalDeleterCalled = true; delete p; } }; using local_unique_ptr = eastl::unique_ptr; local_unique_ptr pEmpty{nullptr}; pEmpty = local_unique_ptr{new int(42), LocalDeleter()}; EATEST_VERIFY(sLocalDeleterCalled == false); } } { // Test that unique_ptr internal pointer is reset before calling the destructor CheckUPtrEmptyInDestructor::mCheckUPtrEmpty = false; unique_ptr uptr(new CheckUPtrEmptyInDestructor); uptr->mpUPtr = &uptr; uptr.reset(); EATEST_VERIFY(CheckUPtrEmptyInDestructor::mCheckUPtrEmpty); } { // Test that unique_ptr<[]> internal pointer is reset before calling the destructor CheckUPtrArrayEmptyInDestructor::mCheckUPtrEmpty = false; unique_ptr uptr(new CheckUPtrArrayEmptyInDestructor[1]); uptr[0].mpUPtr = &uptr; uptr.reset(); EATEST_VERIFY(CheckUPtrArrayEmptyInDestructor::mCheckUPtrEmpty); } { #if EASTL_CORE_ALLOCATOR_ENABLED // Test EA::Allocator::EASTLICoreDeleter usage within eastl::shared_ptr. // http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr // Consider the following for standards compliance. // eastl::shared_ptr foo(pA, EASTLCoreDeleterAdapter()); const int cacheAllocationCount = gEASTLTest_AllocationCount; using namespace EA::Allocator; EASTLCoreAllocatorAdapter ta; void* pMem = ta.allocate(sizeof(A)); EATEST_VERIFY(pMem != nullptr); EATEST_VERIFY(gEASTLTest_AllocationCount > cacheAllocationCount); { A* pA = new (pMem) A(); eastl::shared_ptr foo(pA, EASTLCoreDeleterAdapter()); // Not standards complaint code. Update EASTL implementation to provide the type of the deleter. } EATEST_VERIFY(gEASTLTest_AllocationCount == cacheAllocationCount); EATEST_VERIFY(A::mCount == 0); #endif } { // Test array specialization of unique_ptr EATEST_VERIFY(A::mCount == 0); // template // explicit unique_ptr(P pValue) noexcept unique_ptr pT1(new int[5]); pT1[0] = 5; EATEST_VERIFY(pT1[0] == 5); // Arrays of derived types are not allowed (unlike regular unique_ptr) // unique_ptr pT1B(new B[5]); // Disabled because it should not compile. // (reference) operator[]() const pT1[1] = 1; EATEST_VERIFY(pT1[1] == 1); // explicit unique_ptr(pointer pValue) noexcept unique_ptr pT2(new A[1]); pT2[0].mc = 1; EATEST_VERIFY(pT2[0].mc == 1); EATEST_VERIFY(A::mCount == 1); // pointer operator->() const noexcept pT2[0].mc = 5; EATEST_VERIFY(pT2[0].mc == 5); // void reset(pointer pValue = pointer()) noexcept pT2.reset(new A[2]); pT2[0].mc = 2; EATEST_VERIFY(pT2[0].mc == 2); pT2.reset(0); EATEST_VERIFY(pT2.get() == (A*)0); pT2.reset(new A[3]); pT2[0].mc = 3; EATEST_VERIFY(pT2[0].mc == 3); unique_ptr pT3(new A[4]); pT3[0].mc = 4; EATEST_VERIFY(pT3[0].mc == 4); // void swap(this_type& scopedPtr) noexcept pT2.swap(pT3); EATEST_VERIFY(pT2[0].mc == 4); EATEST_VERIFY(pT3[0].mc == 3); // void swap(unique_ptr& scopedPtr1, unique_ptr& scopedPtr2) noexcept swap(pT2, pT3); EATEST_VERIFY(pT2[0].mc == 3); EATEST_VERIFY(pT3[0].mc == 4); EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get())); // pointer release() noexcept unique_ptr pRelease(new A[1]); A* pAArray = pRelease.release(); delete[] pAArray; // constexpr unique_ptr() noexcept unique_ptr pT4; EATEST_VERIFY(pT4.get() == (A*)0); if(pT4) EATEST_VERIFY(pT4.get()); // Will fail if(!(!pT4)) EATEST_VERIFY(pT4.get()); // Will fail pT4.reset(new A[1]); if(!pT4) EATEST_VERIFY(!pT4.get()); // Will fail EATEST_VERIFY(A::mCount == 8); // There were a number of array creations and deletions above that make this so. // unique_ptr(nullptr_t) noexcept unique_ptr pT5(nullptr); EATEST_VERIFY(pT5.get() == (A*)0); // unique_ptr(pointer pValue, deleter) noexcept CustomArrayDeleter customADeleter; unique_ptr pT6(new A[17], customADeleter); pT6[0].mc = 17; EATEST_VERIFY(pT6[0].mc == 17); // unique_ptr(pointer pValue, typename eastl::remove_reference::type&& deleter) noexcept unique_ptr pT7(new A[18], CustomArrayDeleter()); pT7[0].mc = 18; EATEST_VERIFY(pT7[0].mc == 18); // unique_ptr(this_type&& x) noexcept unique_ptr pT8(eastl::move(pT7)); EATEST_VERIFY(pT8[0].mc == 18); // unique_ptr(unique_ptr&& u, ...) unique_ptr > pT9(eastl::move(pT2)); EATEST_VERIFY(pT9[0].mc == 3); // this_type& operator=(this_type&& u) noexcept // operator=(unique_ptr&& u) noexcept //unique_ptr pTVoid; //unique_ptr pTInt(new int(1)); //pTVoid.operator=(eastl::move(pTInt)); // This doesn't work because CustomDeleter doesn't know how to delete void*. Need to rework this test. // this_type& operator=(nullptr_t) noexcept pT6 = nullptr; EATEST_VERIFY(pT6.get() == (A*)0); // unique_ptr<> make_unique(Args&&... args); unique_ptr p = eastl::make_unique("test", "test2"); EATEST_VERIFY(EA::StdC::Strcmp(p->mpName, "test") == 0 && EA::StdC::Strcmp(p->mpName2, "test2") == 0); unique_ptr pArray = eastl::make_unique(4); pArray[0].mpName = "test"; EATEST_VERIFY(EA::StdC::Strcmp(p->mpName, "test") == 0); #ifdef EASTL_TEST_DISABLED_PENDING_SUPPORT { const size_t kAlignedStructAlignment = 512; struct AlignedStruct {} EA_ALIGN(kAlignedStructAlignment); unique_ptr pAlignedStruct = eastl::make_unique(); EATEST_VERIFY_F(intptr_t(pAlignedStruct.get()) % kAlignedStructAlignment == 0, "pAlignedStruct didn't have proper alignment"); } #endif //Expected to not be valid: //unique_ptr p2Array4 = eastl::make_unique(); //p2Array4[0].mpName = "test"; //EATEST_VERIFY(EA::StdC::Strcmp(p2Array4[0].mpName, "test") == 0); } EATEST_VERIFY(A::mCount == 0); // This check verifies that no A instances were lost, which also verifies that the [] version of the deleter was used in all cases. // validate unique_ptr's compressed_pair implementation is working. { const int ARBITRARY_SIZE = 256; static_assert(sizeof(unique_ptr) == sizeof(uintptr_t), ""); static_assert(sizeof(unique_ptr) == sizeof(uintptr_t), ""); // unique_ptr should be the same size as a pointer. The deleter object is empty so the // eastl::compressed_pair implementation will remove that deleter data member from the unique_ptr. { auto deleter = [](void* pMem) { free(pMem); }; unique_ptr sptr(malloc(ARBITRARY_SIZE), deleter); static_assert(sizeof(sptr) == (sizeof(uintptr_t)), "unexpected unique_ptr size"); } // unique_ptr should be larger than a pointer when the deleter functor is capturing state. This state forces // the compressed_pair to cached the data in unique_ptr locally. { int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0; auto deleter = [=](void* pMem) { auto result = (a+b+c+d+e+f); EA_UNUSED(result); free(pMem); }; unique_ptr sptr(malloc(ARBITRARY_SIZE), deleter); static_assert(sizeof(sptr) == ((6 * sizeof(int)) + (sizeof(uintptr_t))), "unexpected unique_ptr size"); } // Simply test moving the one unique pointer to another. // Exercising operator=(T&&) { { unique_ptr ptr(new int(3)); EATEST_VERIFY(ptr.get() && *ptr == 3); unique_ptr newPtr(new int(4)); EATEST_VERIFY(newPtr.get() && *newPtr == 4); ptr = eastl::move(newPtr); // Deletes int(3) and assigns mpValue to int(4) EATEST_VERIFY(ptr.get() && *ptr == 4); EATEST_VERIFY(newPtr.get() == nullptr); } #if EA_HAVE_CPP11_INITIALIZER_LIST { unique_ptr ptr(new int[3]{ 0, 1, 2 }); EATEST_VERIFY(ptr.get() && ptr[0] == 0 && ptr[1] == 1 && ptr[2] == 2); unique_ptr newPtr(new int[3]{ 3, 4, 5 }); EATEST_VERIFY(newPtr.get() && newPtr[0] == 3 && newPtr[1] == 4 && newPtr[2] == 5); ptr = eastl::move(newPtr); // Deletes int(3) and assigns mpValue to int(4) EATEST_VERIFY(ptr.get() && ptr[0] == 3 && ptr[1] == 4 && ptr[2] == 5); EATEST_VERIFY(newPtr.get() == nullptr); } #endif // ToDo: Test move assignment between two convertible types with an is_assignable deleter_type //{ // struct Base {}; // struct Child : public Base {}; // typedef unique_ptr BaseSPtr; // typedef unique_ptr ChildSPtr; // static_assert(!is_array::value, "This test requires a non-array type"); // static_assert(is_convertible::value, "UniquePtr ptr types must be convertible for this test"); // static_assert(is_assignable::value, "Deleter types must be assignable to one another"); // BaseSPtr ptr(new Base); // EATEST_VERIFY(ptr.get()); // unique_ptr newPtr(new Child); // EATEST_VERIFY(newPtr.get()); // ptr = eastl::move(newPtr); // EATEST_VERIFY(ptr); // EATEST_VERIFY(newPtr.get() == nullptr); //} } } return nErrorCount; } static int Test_scoped_ptr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); { EATEST_VERIFY(A::mCount == 0); scoped_ptr pT1(new int(5)); EATEST_VERIFY(*pT1 == 5); *pT1 = 3; EATEST_VERIFY(*pT1 == 3); EATEST_VERIFY(pT1.get() == get_pointer(pT1)); scoped_ptr pT2(new A(1)); EATEST_VERIFY(pT2->mc == 1); EATEST_VERIFY(A::mCount == 1); pT2.reset(new A(2)); EATEST_VERIFY(pT2->mc == 2); pT2.reset(0); EATEST_VERIFY(pT2.get() == (A*)0); EATEST_VERIFY(pT2.get() == get_pointer(pT2)); pT2.reset(new A(3)); EATEST_VERIFY(pT2->mc == 3); scoped_ptr pT3(new A(4)); EATEST_VERIFY(pT3->mc == 4); pT2.swap(pT3); EATEST_VERIFY(pT2->mc == 4); EATEST_VERIFY(pT3->mc == 3); swap(pT2, pT3); EATEST_VERIFY(pT2->mc == 3); EATEST_VERIFY(pT3->mc == 4); EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get())); scoped_ptr pT4; EATEST_VERIFY(pT4.get() == (A*)0); if(pT4) EATEST_VERIFY(pT4.get()); // Will fail if(!(!pT4)) EATEST_VERIFY(pT4.get()); // Will fail pT4.reset(new A(0)); if(!pT4) EATEST_VERIFY(!pT4.get()); // Will fail EATEST_VERIFY(A::mCount == 3); } { // Test the detach function. scoped_ptr ptr(new A); A* pA = ptr.detach(); delete pA; } { scoped_ptr ptr(new int); (void)ptr; } EATEST_VERIFY(A::mCount == 0); return nErrorCount; } static int Test_scoped_array() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); { scoped_array pT1(new int[5]); pT1[0] = 5; EATEST_VERIFY(pT1[0] == 5); EATEST_VERIFY(pT1.get()[0] == 5); scoped_array pT2(new A[2]); EATEST_VERIFY(A::mCount == 2); EATEST_VERIFY(pT2[0].mc == 0); EATEST_VERIFY(pT2.get()[0].mc == 0); EATEST_VERIFY(get_pointer(pT2)[0].mc == 0); pT2.reset(new A[4]); EATEST_VERIFY(A::mCount == 4); if(!pT2) EATEST_VERIFY(!pT2.get()); // Will fail pT2.reset(0); EATEST_VERIFY(A::mCount == 0); if(pT2) EATEST_VERIFY(pT2.get()); // Will fail if(!(!pT2)) EATEST_VERIFY(pT2.get()); // Will fail scoped_array pT3(new A[3]); EATEST_VERIFY(A::mCount == 3); pT2.swap(pT3); EATEST_VERIFY(A::mCount == 3); swap(pT2, pT3); EATEST_VERIFY(A::mCount == 3); EATEST_VERIFY((pT2 < pT3) == (pT2.get() < pT3.get())); EATEST_VERIFY(A::mCount == 3); } { // Test the detach function. scoped_array ptr(new A[6]); A* pArray = ptr.detach(); delete[] pArray; } { scoped_array ptr(new int[6]); (void)ptr; } EATEST_VERIFY(A::mCount == 0); return nErrorCount; } static int Test_shared_ptr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); // Name test. #if EASTLTEST_GETTYPENAME_AVAILABLE //eastl::string sTypeName = GetTypeName::pointer>(); //EA::UnitTest::Report("type name of (typename shared_ptr::pointer): %s", sTypeName.c_str()); //sTypeName = GetTypeName::type>(); //EA::UnitTest::Report("type name of (typename eastl::common_type::type): %s", sTypeName.c_str()); #endif { shared_ptr pT1; EATEST_VERIFY(pT1.get() == NULL); } { shared_ptr pT1(new int(5)); EATEST_VERIFY(*pT1 == 5); EATEST_VERIFY(pT1.get() == get_pointer(pT1)); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1.unique() ); shared_ptr pT2; EATEST_VERIFY(pT1 != pT2); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1.unique()); pT2 = pT1; EATEST_VERIFY(pT1.use_count() == 2); EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(!pT1.unique()); EATEST_VERIFY(!(pT1 < pT2)); // They should be equal EATEST_VERIFY(pT1 == pT2); *pT1 = 3; EATEST_VERIFY(*pT1 == 3); EATEST_VERIFY(*pT1 == 3); EATEST_VERIFY(*pT2 == 3); pT2.reset((int*)NULL); EATEST_VERIFY(pT2.unique()); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT1.unique()); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1 != pT2); } { EATEST_VERIFY(A::mCount == 0); shared_ptr pT2(new A(0)); EATEST_VERIFY(A::mCount == 1); EATEST_VERIFY(pT2->mc == 0); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); pT2.reset(new A(1)); EATEST_VERIFY(pT2->mc == 1); EATEST_VERIFY(A::mCount == 1); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); shared_ptr pT3(new A(2)); EATEST_VERIFY(A::mCount == 2); pT2.swap(pT3); EATEST_VERIFY(pT2->mc == 2); EATEST_VERIFY(pT3->mc == 1); EATEST_VERIFY(A::mCount == 2); swap(pT2, pT3); EATEST_VERIFY(pT2->mc == 1); EATEST_VERIFY(pT3->mc == 2); EATEST_VERIFY(A::mCount == 2); if(!pT2) EATEST_VERIFY(!pT2.get()); // Will fail shared_ptr pT4; EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); EATEST_VERIFY(A::mCount == 2); if(pT4) EATEST_VERIFY(pT4.get()); // Will fail if(!(!pT4)) EATEST_VERIFY(pT4.get()); // Will fail pT4 = pT2; EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(pT4.use_count() == 2); EATEST_VERIFY(!pT2.unique()); EATEST_VERIFY(!pT4.unique()); EATEST_VERIFY(A::mCount == 2); EATEST_VERIFY(pT2 == pT4); EATEST_VERIFY(pT2 != pT3); EATEST_VERIFY(!(pT2 < pT4)); // They should be equal shared_ptr pT5(pT4); EATEST_VERIFY(pT4 == pT5); EATEST_VERIFY(pT2.use_count() == 3); EATEST_VERIFY(pT4.use_count() == 3); EATEST_VERIFY(pT5.use_count() == 3); EATEST_VERIFY(!pT5.unique()); pT4 = shared_ptr((A*)NULL); EATEST_VERIFY(pT4.unique()); EATEST_VERIFY(pT4.use_count() == 1); EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(A::mCount == 2); } // Regression test reported by a user. // typename eastl::enable_if::value && eastl::is_convertible::value, this_type&>::type // operator=(unique_ptr && uniquePtr) { { shared_ptr rT1(new A(42)); unique_ptr rT2(new B); // default ctor uses 0 rT2->mc = 115; EATEST_VERIFY(rT1->mc == 42); EATEST_VERIFY(rT2->mc == 115); rT1 = eastl::move(rT2); EATEST_VERIFY(rT1->mc == 115); // EATEST_VERIFY(rT2->mc == 115); // state of object post-move is undefined. } // test the state of the shared_ptr::operator= return { shared_ptr rT1(new A(42)); unique_ptr rT2(new B); // default ctor uses 0 rT2->mc = 115; shared_ptr operatorReturn = (rT1 = eastl::move(rT2)); EATEST_VERIFY(operatorReturn == rT1); EATEST_VERIFY(operatorReturn->mc == 115); // EATEST_VERIFY(rT1->mc == 115); // implied as both are pointing to the same address } } { // Test member template functions. shared_ptr pCC(new GrandChildClass); shared_ptr pPC(pCC); shared_ptr pGCC(static_pointer_cast(pPC)); } { // Test enable_shared_from_this shared_ptr p(new Y); shared_ptr q = p->f(); EATEST_VERIFY(p == q); EATEST_VERIFY(!(p < q || q < p)); // p and q must share ownership shared_ptr bctrlp = shared_ptr(new BCLS); } { // Test static_pointer_cast, etc. shared_ptr pGCC(new GrandChildClass); shared_ptr pPC = static_pointer_cast(pGCC); EATEST_VERIFY(pPC == pGCC); #if EASTL_RTTI_ENABLED shared_ptr pCC = dynamic_pointer_cast(pPC); EATEST_VERIFY(pCC == pGCC); #endif #if !defined(__GNUC__) || (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)... eastl::shared_ptr pVoidPtr = shared_ptr(new ParentClass); shared_ptr ap = const_pointer_cast(static_pointer_cast(pVoidPtr)); #endif //typedef shared_ptr ASPtr; //shared_ptr pVoidPtr = ASPtr(new ParentClass); //ASPtr ap = const_pointer_cast(static_pointer_cast(pVoidPtr)); } { // Test static_shared_pointer_cast, etc. shared_ptr pGCC(new GrandChildClass); shared_ptr pPC = static_shared_pointer_cast*/ >(pGCC); EATEST_VERIFY(pPC == pGCC); #if EASTL_RTTI_ENABLED shared_ptr pCC = dynamic_shared_pointer_cast*/ >(pPC); EATEST_VERIFY(pCC == pGCC); #endif } { // Test smart_ptr_deleter shared_ptr pVoid(new ParentClass, smart_ptr_deleter()); EATEST_VERIFY(pVoid.get() != NULL); pVoid = shared_ptr(new ParentClass, smart_ptr_deleter()); EATEST_VERIFY(pVoid.get() != NULL); } { // Test shared_ptr lambda deleter auto deleter = [](int*) {}; eastl::shared_ptr ptr(nullptr, deleter); EATEST_VERIFY(!ptr); EATEST_VERIFY(ptr.get() == nullptr); } { // Test of shared_ptr #if !defined(__GNUC__) || (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)... shared_ptr voidPtr = shared_ptr(new A1); shared_ptr a1Ptr = const_pointer_cast(static_pointer_cast(voidPtr)); #endif } { // Test of static_pointer_cast shared_ptr bPtr = shared_ptr(new B1); shared_ptr aPtr = static_pointer_cast(bPtr); } { // Test shared_ptr { #if !defined(__GNUC__) || (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)... const char* const pName = "NamedClassTest"; NamedClass* const pNamedClass0 = new NamedClass(pName); EATEST_VERIFY(pNamedClass0->mpName == pName); //shared_ptr > voidPtr(pNamedClass0); shared_ptr voidPtr(pNamedClass0); EATEST_VERIFY(voidPtr.get() == pNamedClass0); NamedClass* const pNamedClass1 = (NamedClass*)voidPtr.get(); EATEST_VERIFY(pNamedClass1->mpName == pName); #endif } { #if !defined(__GNUC__) || (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)... const char* const pName = "NamedClassTest"; NamedClass* const pNamedClass0 = new NamedClass(pName); EATEST_VERIFY(pNamedClass0->mpName == pName); shared_ptr voidPtr(pNamedClass0, smart_ptr_deleter()); EATEST_VERIFY(voidPtr.get() == pNamedClass0); NamedClass* const pNamedClass1 = (NamedClass*)voidPtr.get(); EATEST_VERIFY(pNamedClass1->mpName == pName); #endif } } { const char* const pName1 = "NamedClassTest1"; const char* const pName2 = "NamedClassTest2"; shared_ptr sp(new NamedClass(pName1)); EATEST_VERIFY(!sp == false); EATEST_VERIFY(sp.unique()); EATEST_VERIFY(sp->mpName == pName1); shared_ptr sp2 = sp; EATEST_VERIFY(sp2.use_count() == 2); sp2.reset(new NamedClass(pName2)); EATEST_VERIFY(sp2.use_count() == 1); EATEST_VERIFY(sp.unique()); EATEST_VERIFY(sp2->mpName == pName2); sp.reset(); EATEST_VERIFY(!sp == true); } { // Exception handling tests #if EASTL_EXCEPTIONS_ENABLED try { weak_ptr pWeakA; // leave uninitalized shared_ptr pSharedA(pWeakA); // This should throw eastl::bad_weak_ptr EATEST_VERIFY(false); } catch(eastl::bad_weak_ptr&) { EATEST_VERIFY(true); // This pathway should be taken. } catch(...) { EATEST_VERIFY(false); } ThrowingAllocator throwingAllocator; // Throw on first attempt to allocate. shared_ptr pA0; try { A::mCount = 0; pA0 = eastl::allocate_shared >(throwingAllocator, 'a'); EATEST_VERIFY(false); } catch(std::bad_alloc&) { EATEST_VERIFY(true); // This pathway should be taken. EATEST_VERIFY(pA0.get() == NULL); // The C++11 Standard doesn't seem to require this, but that's how we currently do it until we learn it should be otherwise. EATEST_VERIFY(pA0.use_count() == 0); EATEST_VERIFY(A::mCount == 0); // Verify that there were no surviving A instances since the exception. } catch(...) { EATEST_VERIFY(false); } try { shared_ptr pA1(new A('a'), default_delete(), throwingAllocator); EATEST_VERIFY(false); } catch(std::bad_alloc&) { EATEST_VERIFY(true); // This pathway should be taken. EATEST_VERIFY(A::mCount == 0); } catch(...) { EATEST_VERIFY(false); } #endif } #if EASTL_RTTI_ENABLED { // template // shared_ptr(const shared_ptr& sharedPtr, dynamic_cast_tag); // To do. // template // shared_ptr(const shared_ptr& sharedPtr, dynamic_cast_tag, const UDeleter&); // To do. } #endif EATEST_VERIFY(A::mCount == 0); return nErrorCount; } #if EASTL_THREAD_SUPPORT_AVAILABLE // C++ Standard section 20.7.2.5 -- shared_ptr atomic access // shared_ptr thread safety is about safe use of the pointer itself and not about what it points to. shared_ptr thread safety // allows you to safely use shared_ptr from different threads, but if the object shared_ptr holds requires thread safety then // you need to separately handle that in a thread-safe way. A good way to think about it is this: "shared_ptr is as thread-safe as a raw pointer." // // Some helper links: // http://stackoverflow.com/questions/9127816/stdshared-ptr-thread-safety-explained // http://stackoverflow.com/questions/14482830/stdshared-ptr-thread-safety // http://cppwisdom.quora.com/shared_ptr-is-almost-thread-safe // // Test the ability of Futex to report the callstack of another thread holding a futex. struct SharedPtrTestThread : public EA::Thread::IRunnable { EA::Thread::ThreadParameters mThreadParams; EA::Thread::Thread mThread; volatile bool mbShouldContinue; int mnErrorCount; eastl::shared_ptr* mpSPTO; eastl::weak_ptr* mpWPTO; SharedPtrTestThread() : mThreadParams(), mThread(), mbShouldContinue(true), mnErrorCount(0), mpSPTO(NULL), mpWPTO(NULL) {} SharedPtrTestThread(const SharedPtrTestThread&){} void operator=(const SharedPtrTestThread&){} intptr_t Run(void*) { int& nErrorCount = mnErrorCount; // declare nErrorCount so that EATEST_VERIFY can work, as it depends on it being declared. while(mbShouldContinue) { EA::UnitTest::ThreadSleepRandom(1, 10); EATEST_VERIFY(mpSPTO->get()->mX == 99); eastl::shared_ptr temp(mpWPTO->lock()); EATEST_VERIFY(temp->mX == 99); eastl::shared_ptr spTO2(*mpSPTO); EATEST_VERIFY(spTO2->mX == 99); EATEST_VERIFY(spTO2.use_count() >= 2); eastl::weak_ptr wpTO2(spTO2); temp = mpWPTO->lock(); EATEST_VERIFY(temp->mX == 99); temp = spTO2; spTO2.reset(); EATEST_VERIFY(mpSPTO->get()->mX == 99); } return nErrorCount; } }; #endif static int Test_shared_ptr_thread() { using namespace SmartPtrTest; using namespace eastl; using namespace EA::Thread; int nErrorCount(0); #if EASTL_THREAD_SUPPORT_AVAILABLE { SharedPtrTestThread thread[4]; shared_ptr spTO(new TestObject(99)); weak_ptr wpTO(spTO); for(size_t i = 0; i < EAArrayCount(thread); i++) { thread[i].mpSPTO = &spTO; thread[i].mpWPTO = &wpTO; thread[i].mThreadParams.mpName = "SharedPtrTestThread"; } for(size_t i = 0; i < EAArrayCount(thread); i++) thread[i].mThread.Begin(&thread[0], NULL, &thread[0].mThreadParams); EA::UnitTest::ThreadSleep(2000); for(size_t i = 0; i < EAArrayCount(thread); i++) thread[i].mbShouldContinue = false; for(size_t i = 0; i < EAArrayCount(thread); i++) { thread[i].mThread.WaitForEnd(); nErrorCount += thread[i].mnErrorCount; } } #endif #if EASTL_THREAD_SUPPORT_AVAILABLE { // We currently do light testing of the atomic functions. It would take a bit of work to fully test // the memory behavior of these in a rigorous way. Also, as of this writing we don't have a portable // way to use the std::memory_order functionality. shared_ptr spTO(new TestObject(55)); // bool atomic_is_lock_free(const shared_ptr*); EATEST_VERIFY(!atomic_is_lock_free(&spTO)); // shared_ptr atomic_load(const shared_ptr* pSharedPtr); // shared_ptr atomic_load_explicit(const shared_ptr* pSharedPtr, ... /*std::memory_order memoryOrder*/); shared_ptr spTO2 = atomic_load(&spTO); EATEST_VERIFY(spTO->mX == 55); EATEST_VERIFY(spTO2->mX == 55); // void atomic_store(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB); // void atomic_store_explicit(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB, ... /*std::memory_order memoryOrder*/); spTO2->mX = 56; EATEST_VERIFY(spTO->mX == 56); EATEST_VERIFY(spTO2->mX == 56); atomic_store(&spTO, shared_ptr(new TestObject(77))); EATEST_VERIFY(spTO->mX == 77); EATEST_VERIFY(spTO2->mX == 56); // shared_ptr atomic_exchange(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB); // shared_ptr atomic_exchange_explicit(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB, ... /*std::memory_order memoryOrder*/); spTO = atomic_exchange(&spTO2, spTO); EATEST_VERIFY(spTO->mX == 56); EATEST_VERIFY(spTO2->mX == 77); spTO = atomic_exchange_explicit(&spTO2, spTO); EATEST_VERIFY(spTO->mX == 77); EATEST_VERIFY(spTO2->mX == 56); // bool atomic_compare_exchange_strong(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew); // bool atomic_compare_exchange_weak(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew); // bool atomic_compare_exchange_strong_explicit(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew, ... /*memory_order memoryOrderSuccess, memory_order memoryOrderFailure*/); // bool atomic_compare_exchange_weak_explicit(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew, ... /*memory_order memoryOrderSuccess, memory_order memoryOrderFailure*/); shared_ptr spTO3 = atomic_load(&spTO2); bool result = atomic_compare_exchange_strong(&spTO3, &spTO, make_shared(88)); // spTO3 != spTO, so this should do no exchange and return false. EATEST_VERIFY(!result); EATEST_VERIFY(spTO3->mX == 56); EATEST_VERIFY(spTO->mX == 56); result = atomic_compare_exchange_strong(&spTO3, &spTO2, make_shared(88)); // spTO3 == spTO2, so this should succeed. EATEST_VERIFY(result); EATEST_VERIFY(spTO2->mX == 56); EATEST_VERIFY(spTO3->mX == 88); } #endif EATEST_VERIFY(A::mCount == 0); TestObject::Reset(); return nErrorCount; } static int Test_weak_ptr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); { weak_ptr pW0; shared_ptr pS0(new int(0)); shared_ptr pS1(new int(1)); weak_ptr pW1(pS1); weak_ptr pW2; weak_ptr pW3(pW2); EATEST_VERIFY(pS1.use_count() == 1); EATEST_VERIFY(pW1.use_count() == 1); EATEST_VERIFY(pW2.use_count() == 0); EATEST_VERIFY(pW3.use_count() == 0); EATEST_VERIFY(pW1.expired() == false); EATEST_VERIFY(pW2.expired() == true); EATEST_VERIFY(pW3.expired() == true); pS1.reset(); EATEST_VERIFY(pW1.expired() == true); pW1 = pS0; EATEST_VERIFY(pW1.expired() == false); pW1.swap(pW2); EATEST_VERIFY(pW1.expired() == true); EATEST_VERIFY(pW2.expired() == false); pW1 = pW2; EATEST_VERIFY(pW1.expired() == false); pW3 = pW1; EATEST_VERIFY(pW3.expired() == false); EATEST_VERIFY(pS1.use_count() == 0); pW3.reset(); EATEST_VERIFY(pW3.expired() == true); pS1.reset(new int(3)); EATEST_VERIFY(pS1.use_count() == 1); pW3 = pS1; EATEST_VERIFY(pS1.use_count() == 1); EATEST_VERIFY(pS1.use_count() == pW3.use_count()); shared_ptr pShared2(pW2.lock()); shared_ptr pShared3(pW3.lock()); EATEST_VERIFY(pShared2.use_count() == 2); EATEST_VERIFY(pShared3.use_count() == 2); swap(pW2, pW3); EATEST_VERIFY(pW2.use_count() == 2); EATEST_VERIFY(pW3.use_count() == 2); pW1 = pW3; EATEST_VERIFY(pW3.use_count() == 2); EATEST_VERIFY((pW2 < pW3) || (pW3 < pW2)); EATEST_VERIFY(pS0.use_count() == 2); pW0 = pS0; // This tests the deletion of a weak_ptr after its associated shared_ptr has destructed. EATEST_VERIFY(pS0.use_count() == 2); } { weak_ptr wp; EATEST_VERIFY(wp.use_count() == 0); EATEST_VERIFY(wp.expired() == true); { shared_ptr sp(new NamedClass("NamedClass")); wp = sp; EATEST_VERIFY(wp.use_count() == 1); EATEST_VERIFY(wp.expired() == false); } EATEST_VERIFY(wp.use_count() == 0); EATEST_VERIFY(wp.expired() == true); } { // shared_from_this // This example is taken from the C++11 Standard doc. shared_ptr pFoo(new foo); shared_ptr qFoo = pFoo->shared_from_this(); EATEST_VERIFY(pFoo == qFoo); EATEST_VERIFY(!(pFoo < qFoo) && !(qFoo < pFoo)); // p and q share ownership } { // weak_from_this const shared_ptr pFoo(new foo); weak_ptr qFoo = pFoo->weak_from_this(); EATEST_VERIFY(pFoo == qFoo.lock()); EATEST_VERIFY(!(pFoo < qFoo.lock()) && !(qFoo.lock() < pFoo)); // p and q share ownership } { // weak_from_this shared_ptr pFoo(new foo); weak_ptr qFoo = pFoo->weak_from_this(); EATEST_VERIFY(pFoo == qFoo.lock()); EATEST_VERIFY(!(pFoo < qFoo.lock()) && !(qFoo.lock() < pFoo)); // p and q share ownership } return nErrorCount; } static int Test_shared_array() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); { shared_array pT1(new int[5]); pT1[0] = 5; EATEST_VERIFY(pT1[0] == 5); EATEST_VERIFY(pT1.get() == get_pointer(pT1)); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1.unique()); shared_array pT2; EATEST_VERIFY(pT1 != pT2); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1.unique()); pT2 = pT1; EATEST_VERIFY(pT1.use_count() == 2); EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(!pT1.unique()); EATEST_VERIFY(!(pT1 < pT2)); // They should be equal EATEST_VERIFY(pT1 == pT2); *pT1 = 3; EATEST_VERIFY(*pT1 == 3); EATEST_VERIFY(*pT1 == 3); EATEST_VERIFY(*pT2 == 3); pT2.reset(0); EATEST_VERIFY(pT2.unique()); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT1.unique()); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1 != pT2); } { EATEST_VERIFY(A::mCount == 0); shared_array pT2(new A[5]); EATEST_VERIFY(A::mCount == 5); EATEST_VERIFY(pT2->mc == 0); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); pT2.reset(new A[1]); pT2[0].mc = 1; EATEST_VERIFY(pT2->mc == 1); EATEST_VERIFY(A::mCount == 1); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); shared_array pT3(new A[2]); EATEST_VERIFY(A::mCount == 3); pT2.swap(pT3); pT2[0].mc = 2; EATEST_VERIFY(pT2->mc == 2); EATEST_VERIFY(pT3->mc == 1); EATEST_VERIFY(A::mCount == 3); swap(pT2, pT3); EATEST_VERIFY(pT2->mc == 1); EATEST_VERIFY(pT3->mc == 2); EATEST_VERIFY(A::mCount == 3); if(!pT2) EATEST_VERIFY(!pT2.get()); // Will fail shared_array pT4; EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); EATEST_VERIFY(A::mCount == 3); if(pT4) EATEST_VERIFY(pT4.get()); // Will fail if(!(!pT4)) EATEST_VERIFY(pT4.get()); // Will fail pT4 = pT2; EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(pT4.use_count() == 2); EATEST_VERIFY(!pT2.unique()); EATEST_VERIFY(!pT4.unique()); EATEST_VERIFY(A::mCount == 3); EATEST_VERIFY(pT2 == pT4); EATEST_VERIFY(pT2 != pT3); EATEST_VERIFY(!(pT2 < pT4)); // They should be equal shared_array pT5(pT4); EATEST_VERIFY(pT4 == pT5); EATEST_VERIFY(pT2.use_count() == 3); EATEST_VERIFY(pT4.use_count() == 3); EATEST_VERIFY(pT5.use_count() == 3); EATEST_VERIFY(!pT5.unique()); pT4 = shared_array(0); EATEST_VERIFY(pT4.unique()); EATEST_VERIFY(pT4.use_count() == 1); EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(A::mCount == 3); } EATEST_VERIFY(A::mCount == 0); return nErrorCount; } static int Test_linked_ptr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); { linked_ptr pT1(new int(5)); EATEST_VERIFY(*pT1.get() == 5); EATEST_VERIFY(pT1.get() == get_pointer(pT1)); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1.unique()); linked_ptr pT2; EATEST_VERIFY(pT1 != pT2); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1.unique()); pT2 = pT1; EATEST_VERIFY(pT1.use_count() == 2); EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(!pT1.unique()); EATEST_VERIFY(!(pT1 < pT2)); // They should be equal EATEST_VERIFY(pT1 == pT2); *pT1 = 3; EATEST_VERIFY(*pT1.get() == 3); EATEST_VERIFY(*pT1 == 3); EATEST_VERIFY(*pT2 == 3); pT2.reset((int*)NULL); EATEST_VERIFY(pT2.unique()); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT1.unique()); EATEST_VERIFY(pT1.use_count() == 1); EATEST_VERIFY(pT1 != pT2); } { EATEST_VERIFY(A::mCount == 0); linked_ptr pT2(new A(0)); EATEST_VERIFY(A::mCount == 1); EATEST_VERIFY(pT2->mc == 0); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); pT2.reset(new A(1)); EATEST_VERIFY(pT2->mc == 1); EATEST_VERIFY(A::mCount == 1); EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); linked_ptr pT3(new A(2)); EATEST_VERIFY(A::mCount == 2); linked_ptr pT4; EATEST_VERIFY(pT2.use_count() == 1); EATEST_VERIFY(pT2.unique()); EATEST_VERIFY(A::mCount == 2); if(pT4) EATEST_VERIFY(pT4.get()); // Will fail if(!(!pT4)) EATEST_VERIFY(pT4.get()); // Will fail pT4 = pT2; EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(pT4.use_count() == 2); EATEST_VERIFY(!pT2.unique()); EATEST_VERIFY(!pT4.unique()); EATEST_VERIFY(A::mCount == 2); EATEST_VERIFY(pT2 == pT4); EATEST_VERIFY(pT2 != pT3); EATEST_VERIFY(!(pT2 < pT4)); // They should be equal linked_ptr pT5(pT4); EATEST_VERIFY(pT4 == pT5); EATEST_VERIFY(pT2.use_count() == 3); EATEST_VERIFY(pT4.use_count() == 3); EATEST_VERIFY(pT5.use_count() == 3); EATEST_VERIFY(!pT5.unique()); pT4 = linked_ptr((A*)NULL); EATEST_VERIFY(pT4.unique()); EATEST_VERIFY(pT4.use_count() == 1); EATEST_VERIFY(pT2.use_count() == 2); EATEST_VERIFY(A::mCount == 2); } { // Do some force_delete tests. linked_ptr pT2(new A(0)); linked_ptr pT3(pT2); pT2.force_delete(); pT3.force_delete(); } EATEST_VERIFY(A::mCount == 0); { // Verify that subclasses are usable. bool bAlloc = false; eastl::linked_ptr pDMO(new DerivedMockObject(&bAlloc)); eastl::linked_ptr a1(pDMO); eastl::linked_ptr a2; a2 = pDMO; } { // Test regression for a bug. linked_ptr pT2; linked_ptr pT3(pT2); // In the bug linked_ptr::mpPrev and mpNext were not initialized via this ctor. pT3.reset(new A); // In the bug this would crash due to unintialized mpPrev/mpNext. linked_ptr pT4; linked_ptr pT5(pT4); pT5.reset(new A); linked_array pT6; linked_array pT7(pT6); pT7.reset(new A[1]); } return nErrorCount; } static int Test_linked_array() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount(0); { // Tests go here. } { // Do some force_delete tests. linked_array pT2(new A[2]); linked_array pT3(pT2); pT2.force_delete(); pT3.force_delete(); } EATEST_VERIFY(A::mCount == 0); return nErrorCount; } static int Test_intrusive_ptr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount = 0; { // Test ctor/dtor intrusive_ptr ip1; intrusive_ptr ip2(NULL, false); intrusive_ptr ip3(NULL, true); intrusive_ptr ip4(new RefCountTest, true); intrusive_ptr ip5(new RefCountTest, false); intrusive_ptr ip6(ip1); intrusive_ptr ip7(ip4); EATEST_VERIFY(ip1.get() == NULL); EATEST_VERIFY(!ip1); EATEST_VERIFY(ip2.get() == NULL); EATEST_VERIFY(!ip2); EATEST_VERIFY(ip3.get() == NULL); EATEST_VERIFY(!ip3); EATEST_VERIFY(ip4.get() != NULL); EATEST_VERIFY(ip4.get()->mRefCount == 2); EATEST_VERIFY(ip4); EATEST_VERIFY(ip5.get() != NULL); EATEST_VERIFY(ip5.get()->mRefCount == 0); ip5.get()->AddRef(); EATEST_VERIFY(ip5.get()->mRefCount == 1); EATEST_VERIFY(ip5); EATEST_VERIFY(ip6.get() == NULL); EATEST_VERIFY(!ip6); EATEST_VERIFY(ip7.get() != NULL); EATEST_VERIFY(ip7.get()->mRefCount == 2); EATEST_VERIFY(ip7); } { // Test move-ctor { VERIFY(RefCountTest::mCount == 0); intrusive_ptr ip1(new RefCountTest); VERIFY(RefCountTest::mCount == 1); VERIFY(ip1->mRefCount == 1); { intrusive_ptr ip2(eastl::move(ip1)); VERIFY(ip1.get() != ip2.get()); VERIFY(ip2->mRefCount == 1); VERIFY(RefCountTest::mCount == 1); } VERIFY(ip1.get() == nullptr); VERIFY(RefCountTest::mCount == 0); } // Test move-assignment { VERIFY(RefCountTest::mCount == 0); intrusive_ptr ip1(new RefCountTest); VERIFY(RefCountTest::mCount == 1); VERIFY(ip1->mRefCount == 1); { intrusive_ptr ip2; ip2 = eastl::move(ip1); VERIFY(ip1.get() != ip2.get()); VERIFY(ip2->mRefCount == 1); VERIFY(RefCountTest::mCount == 1); } VERIFY(ip1.get() == nullptr); VERIFY(RefCountTest::mCount == 0); } } { // Test modifiers (assign, attach, detach, reset, swap) RefCountTest* const p1 = new RefCountTest; RefCountTest* const p2 = new RefCountTest; intrusive_ptr ip1; intrusive_ptr ip2; ip1 = p1; ip2 = p2; EATEST_VERIFY(ip1.get() == p1); EATEST_VERIFY((*ip1).mRefCount == 1); EATEST_VERIFY(ip1->mRefCount == 1); ip1.detach(); EATEST_VERIFY(ip1.get() == NULL); ip1.attach(p1); EATEST_VERIFY(ip1.get() == p1); EATEST_VERIFY(ip1->mRefCount == 1); ip1.swap(ip2); EATEST_VERIFY(ip1.get() == p2); EATEST_VERIFY(ip2.get() == p1); ip1.swap(ip2); ip1 = ip2; EATEST_VERIFY(ip1 == p2); ip1.reset(); EATEST_VERIFY(ip1.get() == NULL); EATEST_VERIFY(ip2.get() == p2); ip2.reset(); EATEST_VERIFY(ip2.get() == NULL); } { // Test external functions intrusive_ptr ip1; intrusive_ptr ip2(new RefCountTest); intrusive_ptr ip3(ip1); intrusive_ptr ip4(ip2); // The VC++ code scanner crashes when it scans this code. EATEST_VERIFY(get_pointer(ip1) == NULL); EATEST_VERIFY(get_pointer(ip2) != NULL); EATEST_VERIFY(get_pointer(ip3) == get_pointer(ip1)); EATEST_VERIFY(get_pointer(ip4) == get_pointer(ip2)); EATEST_VERIFY(ip3 == ip1); EATEST_VERIFY(ip4 == ip2); EATEST_VERIFY(ip1 == ip3); EATEST_VERIFY(ip2 == ip4); EATEST_VERIFY(ip1 != ip2); EATEST_VERIFY(ip3 != ip4); EATEST_VERIFY(ip2 != ip1); EATEST_VERIFY(ip4 != ip3); EATEST_VERIFY(ip3 == ip1.get()); EATEST_VERIFY(ip4 == ip2.get()); EATEST_VERIFY(ip1 == ip3.get()); EATEST_VERIFY(ip2 == ip4.get()); EATEST_VERIFY(ip1 != ip2.get()); EATEST_VERIFY(ip3 != ip4.get()); EATEST_VERIFY(ip2 != ip1.get()); EATEST_VERIFY(ip4 != ip3.get()); EATEST_VERIFY(ip3.get() == ip1); EATEST_VERIFY(ip4.get() == ip2); EATEST_VERIFY(ip1.get() == ip3); EATEST_VERIFY(ip2.get() == ip4); EATEST_VERIFY(ip1.get() != ip2); EATEST_VERIFY(ip3.get() != ip4); EATEST_VERIFY(ip2.get() != ip1); EATEST_VERIFY(ip4.get() != ip3); EATEST_VERIFY((ip4 < ip3) || (ip3 < ip4)); swap(ip1, ip3); EATEST_VERIFY(get_pointer(ip3) == get_pointer(ip1)); swap(ip2, ip4); EATEST_VERIFY(get_pointer(ip2) == get_pointer(ip4)); swap(ip1, ip2); EATEST_VERIFY(get_pointer(ip1) != NULL); EATEST_VERIFY(get_pointer(ip2) == NULL); EATEST_VERIFY(get_pointer(ip1) == get_pointer(ip4)); EATEST_VERIFY(get_pointer(ip2) == get_pointer(ip3)); } { // Misc tests. intrusive_ptr ip; EATEST_VERIFY(ip.get() == NULL); ip.reset(); EATEST_VERIFY(ip.get() == NULL); intrusive_ptr ip2(NULL, false); EATEST_VERIFY(ip.get() == NULL); bool boolValue = false; Test* pTest = new Test(&boolValue); EATEST_VERIFY(boolValue); pTest->AddRef(); intrusive_ptr ip3(pTest, false); EATEST_VERIFY(ip3.get() == pTest); ip3.reset(); EATEST_VERIFY(!boolValue); } { // Misc tests. bool boolArray[3]; memset(boolArray, 0, sizeof(boolArray)); Test* p1 = new Test(boolArray + 0); EATEST_VERIFY(boolArray[0] && !boolArray[1] && !boolArray[2]); intrusive_ptr arc1(p1); EATEST_VERIFY(boolArray[0] && !boolArray[1] && !boolArray[2]); Test* p2 = new Test(boolArray + 1); EATEST_VERIFY(boolArray[0] && boolArray[1] && !boolArray[2]); arc1 = p2; EATEST_VERIFY(!boolArray[0] && boolArray[1] && !boolArray[2]); Test* p3 = new Test(boolArray + 2); EATEST_VERIFY(!boolArray[0] && boolArray[1] && boolArray[2]); arc1 = p3; EATEST_VERIFY(!boolArray[0] && !boolArray[1] && boolArray[2]); arc1 = NULL; EATEST_VERIFY(!boolArray[0] && !boolArray[1] && !boolArray[2]); } { // Test intrusive_ptr_add_ref() / intrusive_ptr_release() IntrusiveCustom* const pIC = new IntrusiveCustom; { intrusive_ptr bp = intrusive_ptr(pIC); intrusive_ptr ap = bp; } EATEST_VERIFY((IntrusiveCustom::mAddRefCallCount > 0) && (IntrusiveCustom::mReleaseCallCount == IntrusiveCustom::mAddRefCallCount)); } { // Regression intrusive_ptr bp = intrusive_ptr(new IntrusiveChild); intrusive_ptr ap = bp; } return nErrorCount; } struct RandomLifetimeObject : public eastl::safe_object { void DoSomething() const { } }; static int Test_safe_ptr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount = 0; { // non-const RandomLifetimeObject RandomLifetimeObject* pObject = new RandomLifetimeObject; eastl::safe_ptr pSafePtr(pObject); eastl::safe_ptr pSafePtrCopy1 = pSafePtr; eastl::safe_ptr pSafePtrCopy2(pSafePtr); pSafePtr->DoSomething(); eastl::safe_ptr* pSafePtrCopy3 = new eastl::safe_ptr(pSafePtr); eastl::safe_ptr* pSafePtrCopy4 = new eastl::safe_ptr(pSafePtr); EATEST_VERIFY(pSafePtrCopy3->get() == pObject); EATEST_VERIFY(pSafePtrCopy4->get() == pObject); delete pSafePtrCopy3; delete pSafePtrCopy4; delete pSafePtr; EATEST_VERIFY(pSafePtrCopy1.get() == NULL); EATEST_VERIFY(pSafePtrCopy2.get() == NULL); } { // const RandomLifetimeObject RandomLifetimeObject* pObject = new RandomLifetimeObject; eastl::safe_ptr pSafePtr(pObject); eastl::safe_ptr pSafePtrCopy1(pSafePtr); eastl::safe_ptr pSafePtrCopy2 = pSafePtr; pSafePtr->DoSomething(); eastl::safe_ptr* pSafePtrCopy3 = new eastl::safe_ptr(pSafePtr); eastl::safe_ptr* pSafePtrCopy4 = new eastl::safe_ptr(pSafePtr); EATEST_VERIFY(pSafePtrCopy3->get() == pObject); EATEST_VERIFY(pSafePtrCopy4->get() == pObject); delete pSafePtrCopy3; delete pSafePtrCopy4; delete pSafePtr; EATEST_VERIFY(pSafePtrCopy1.get() == NULL); EATEST_VERIFY(pSafePtrCopy2.get() == NULL); } return nErrorCount; } int TestSmartPtr() { using namespace SmartPtrTest; using namespace eastl; int nErrorCount = 0; nErrorCount += Test_unique_ptr(); nErrorCount += Test_scoped_ptr(); nErrorCount += Test_scoped_array(); nErrorCount += Test_shared_ptr(); nErrorCount += Test_shared_ptr_thread(); nErrorCount += Test_weak_ptr(); nErrorCount += Test_shared_array(); nErrorCount += Test_linked_ptr(); nErrorCount += Test_linked_array(); nErrorCount += Test_intrusive_ptr(); nErrorCount += Test_safe_ptr(); EATEST_VERIFY(A::mCount == 0); EATEST_VERIFY(RefCountTest::mCount == 0); EATEST_VERIFY(NamedClass::mnCount == 0); EATEST_VERIFY(Y::mnCount == 0); EATEST_VERIFY(ACLS::mnCount == 0); EATEST_VERIFY(BCLS::mnCount == 0); EATEST_VERIFY(A1::mnCount == 0); EATEST_VERIFY(B1::mnCount == 0); return nErrorCount; } EA_RESTORE_VC_WARNING() // 4702