aboutsummaryrefslogtreecommitdiff
path: root/test/source/TestSmartPtr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/source/TestSmartPtr.cpp')
1 files changed, 2197 insertions, 0 deletions
diff --git a/test/source/TestSmartPtr.cpp b/test/source/TestSmartPtr.cpp
new file mode 100644
index 0000000..dc94b96
--- /dev/null
+++ b/test/source/TestSmartPtr.cpp
@@ -0,0 +1,2197 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include "EASTLTest.h"
+#include "GetTypeName.h"
+#include <EAStdC/EAString.h>
+#include <EAStdC/EAStopwatch.h>
+#include <EASTL/core_allocator_adapter.h>
+#include <EASTL/core_allocator.h>
+#include <EASTL/intrusive_ptr.h>
+#include <EASTL/linked_array.h>
+#include <EASTL/linked_ptr.h>
+#include <EASTL/safe_ptr.h>
+#include <EASTL/scoped_array.h>
+#include <EASTL/scoped_ptr.h>
+#include <EASTL/shared_array.h>
+#include <EASTL/shared_ptr.h>
+#include <EASTL/unique_ptr.h>
+#include <EASTL/weak_ptr.h>
+#include <eathread/eathread_thread.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stdio.h>
+#include <string.h>
+#ifdef EA_PLATFORM_WINDOWS
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #include <Windows.h>
+#elif defined(EA_PLATFORM_ANDROID)
+ #include <android/log.h>
+#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 <typename T>
+ 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<T*>(p); }
+
+ CustomDeleter() {}
+ CustomDeleter(const CustomDeleter&) {}
+ CustomDeleter(CustomDeleter&&) {}
+ CustomDeleter& operator=(const CustomDeleter&) { return *this; }
+ CustomDeleter& operator=(CustomDeleter&&) { return *this; }
+ };
+
+
+ struct CustomArrayDeleter
+ {
+ template <typename T>
+ 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<T*>(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<Y>
+ {
+ static int mnCount;
+
+ Y() { ++mnCount; }
+ Y(const Y&) { ++mnCount; }
+ Y& operator=(const Y&) { return *this; }
+ ~Y() { --mnCount; }
+
+ eastl::shared_ptr<Y> 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<ACLS>
+ {
+ 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>
+ {
+ foo() : mX(0){}
+ int mX;
+ };
+
+ struct CheckUPtrEmptyInDestructor
+ {
+ ~CheckUPtrEmptyInDestructor()
+ {
+ if(mpUPtr)
+ mCheckUPtrEmpty = (*mpUPtr == nullptr);
+ }
+
+ eastl::unique_ptr<CheckUPtrEmptyInDestructor>* mpUPtr{};
+ static bool mCheckUPtrEmpty;
+ };
+
+ bool CheckUPtrEmptyInDestructor::mCheckUPtrEmpty = false;
+
+ struct CheckUPtrArrayEmptyInDestructor
+ {
+ ~CheckUPtrArrayEmptyInDestructor()
+ {
+ if(mpUPtr)
+ mCheckUPtrEmpty = (*mpUPtr == nullptr);
+ }
+
+ eastl::unique_ptr<CheckUPtrArrayEmptyInDestructor[]>* 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<int> 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<A> 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<A> 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<A> 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<T, D>& scopedPtr1, unique_ptr<T, D>& 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<A> pRelease(new A);
+ EATEST_VERIFY(A::mCount == 3);
+ pA = pRelease.release();
+ delete pA;
+ EATEST_VERIFY(A::mCount == 2);
+
+ // constexpr unique_ptr() noexcept
+ unique_ptr<A> 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<A> pT5(nullptr);
+ EATEST_VERIFY(pT5.get() == (A*)0);
+
+ // unique_ptr(pointer pValue, deleter) noexcept
+ CustomDeleter customADeleter;
+ unique_ptr<A, CustomDeleter> pT6(new A(17), customADeleter);
+ EATEST_VERIFY(pT6->mc == 17);
+
+ // unique_ptr(pointer pValue, typename eastl::remove_reference<Deleter>::type&& deleter) noexcept
+ unique_ptr<A, CustomDeleter> pT7(new A(18), CustomDeleter());
+ EATEST_VERIFY(pT7->mc == 18);
+
+ // unique_ptr(this_type&& x) noexcept
+ unique_ptr<A, CustomDeleter> pT8(eastl::move(pT7));
+ EATEST_VERIFY(pT8->mc == 18);
+
+ // unique_ptr(unique_ptr<U, E>&& u, ...)
+ unique_ptr<A, default_delete<A> > pT9(eastl::move(pT2));
+
+ // this_type& operator=(this_type&& u) noexcept
+ // operator=(unique_ptr<U, E>&& u) noexcept
+ //unique_ptr<void, CustomDeleter> pTVoid;
+ //unique_ptr<int, CustomDeleter> pTInt(new int(1));
+ //pTVoid.operator=<int, CustomDeleter>(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<int, LocalDeleter>;
+
+ 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<CheckUPtrEmptyInDestructor> 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<CheckUPtrArrayEmptyInDestructor[]> 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<A, EASTLCoreDeleterAdapter> 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<A> 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 <typename P>
+ // explicit unique_ptr(P pValue) noexcept
+ unique_ptr<int[]> 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<A[]> 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<A[]> 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<A[]> 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<T, D>& scopedPtr1, unique_ptr<T, D>& 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<A[]> pRelease(new A[1]);
+ A* pAArray = pRelease.release();
+ delete[] pAArray;
+
+ // constexpr unique_ptr() noexcept
+ unique_ptr<A[]> 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<A[]> pT5(nullptr);
+ EATEST_VERIFY(pT5.get() == (A*)0);
+
+ // unique_ptr(pointer pValue, deleter) noexcept
+ CustomArrayDeleter customADeleter;
+ unique_ptr<A[], CustomArrayDeleter> pT6(new A[17], customADeleter);
+ pT6[0].mc = 17;
+ EATEST_VERIFY(pT6[0].mc == 17);
+
+ // unique_ptr(pointer pValue, typename eastl::remove_reference<Deleter>::type&& deleter) noexcept
+ unique_ptr<A[], CustomArrayDeleter> pT7(new A[18], CustomArrayDeleter());
+ pT7[0].mc = 18;
+ EATEST_VERIFY(pT7[0].mc == 18);
+
+ // unique_ptr(this_type&& x) noexcept
+ unique_ptr<A[], CustomArrayDeleter> pT8(eastl::move(pT7));
+ EATEST_VERIFY(pT8[0].mc == 18);
+
+ // unique_ptr(unique_ptr<U, E>&& u, ...)
+ unique_ptr<A[], default_delete<A[]> > pT9(eastl::move(pT2));
+ EATEST_VERIFY(pT9[0].mc == 3);
+
+ // this_type& operator=(this_type&& u) noexcept
+ // operator=(unique_ptr<U, E>&& u) noexcept
+ //unique_ptr<void, CustomDeleter> pTVoid;
+ //unique_ptr<int, CustomDeleter> pTInt(new int(1));
+ //pTVoid.operator=<int, CustomDeleter>(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<NamedClass> p = eastl::make_unique<NamedClass>("test", "test2");
+ EATEST_VERIFY(EA::StdC::Strcmp(p->mpName, "test") == 0 && EA::StdC::Strcmp(p->mpName2, "test2") == 0);
+
+ unique_ptr<NamedClass[]> pArray = eastl::make_unique<NamedClass[]>(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<AlignedStruct> pAlignedStruct = eastl::make_unique<AlignedStruct>();
+ EATEST_VERIFY_F(intptr_t(pAlignedStruct.get()) % kAlignedStructAlignment == 0, "pAlignedStruct didn't have proper alignment");
+ }
+ #endif
+
+ //Expected to not be valid:
+ //unique_ptr<NamedClass[4]> p2Array4 = eastl::make_unique<NamedClass[4]>();
+ //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<short>) == sizeof(uintptr_t), "");
+ static_assert(sizeof(unique_ptr<long>) == 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<void, decltype(deleter)> 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<void, decltype(deleter)> 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<int> ptr(new int(3));
+ EATEST_VERIFY(ptr.get() && *ptr == 3);
+
+ unique_ptr<int> 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<int[]> ptr(new int[3]{ 0, 1, 2 });
+ EATEST_VERIFY(ptr.get() && ptr[0] == 0 && ptr[1] == 1 && ptr[2] == 2);
+
+ unique_ptr<int[]> 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<Base, CustomDeleter> BaseSPtr;
+ // typedef unique_ptr<Child, CustomDeleter> ChildSPtr;
+
+ // static_assert(!is_array<BaseSPtr::element_type>::value, "This test requires a non-array type");
+ // static_assert(is_convertible<ChildSPtr::pointer, BaseSPtr::pointer>::value, "UniquePtr ptr types must be convertible for this test");
+ // static_assert(is_assignable<BaseSPtr::deleter_type&, ChildSPtr::deleter_type&&>::value, "Deleter types must be assignable to one another");
+
+ // BaseSPtr ptr(new Base);
+ // EATEST_VERIFY(ptr.get());
+
+ // unique_ptr<Child> 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<int> pT1(new int(5));
+ EATEST_VERIFY(*pT1 == 5);
+
+ *pT1 = 3;
+ EATEST_VERIFY(*pT1 == 3);
+ EATEST_VERIFY(pT1.get() == get_pointer(pT1));
+
+ scoped_ptr<A> 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<A> 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<A> 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<A> ptr(new A);
+ A* pA = ptr.detach();
+ delete pA;
+ }
+
+ {
+ scoped_ptr<void> 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<int> pT1(new int[5]);
+ pT1[0] = 5;
+ EATEST_VERIFY(pT1[0] == 5);
+ EATEST_VERIFY(pT1.get()[0] == 5);
+
+ scoped_array<A> 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<A> 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<A> ptr(new A[6]);
+ A* pArray = ptr.detach();
+ delete[] pArray;
+ }
+
+ {
+ scoped_array<void> 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<typename eastl::unique_ptr<int>::pointer>();
+ //EA::UnitTest::Report("type name of (typename shared_ptr<int>::pointer): %s", sTypeName.c_str());
+
+ //sTypeName = GetTypeName<typename eastl::common_type<int*, int*>::type>();
+ //EA::UnitTest::Report("type name of (typename eastl::common_type<int*, int*>::type): %s", sTypeName.c_str());
+ #endif
+
+ {
+ shared_ptr<int> pT1;
+ EATEST_VERIFY(pT1.get() == NULL);
+ }
+
+ {
+ shared_ptr<int> 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<int> 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<A> 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<A> 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<A> 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<A> 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>((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<!eastl::is_array<U>::value && eastl::is_convertible<U*, element_type*>::value, this_type&>::type
+ // operator=(unique_ptr<U, Deleter> && uniquePtr)
+ {
+ {
+ shared_ptr<A> rT1(new A(42));
+ unique_ptr<B> 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<A> rT1(new A(42));
+ unique_ptr<B> rT2(new B); // default ctor uses 0
+ rT2->mc = 115;
+
+ shared_ptr<A> 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<ChildClass> pCC(new GrandChildClass);
+ shared_ptr<ParentClass> pPC(pCC);
+ shared_ptr<GrandChildClass> pGCC(static_pointer_cast<GrandChildClass>(pPC));
+ }
+
+
+ { // Test enable_shared_from_this
+ shared_ptr<Y> p(new Y);
+ shared_ptr<Y> q = p->f();
+
+ EATEST_VERIFY(p == q);
+ EATEST_VERIFY(!(p < q || q < p)); // p and q must share ownership
+
+ shared_ptr<BCLS> bctrlp = shared_ptr<BCLS>(new BCLS);
+ }
+
+
+ { // Test static_pointer_cast, etc.
+ shared_ptr<GrandChildClass> pGCC(new GrandChildClass);
+ shared_ptr<ParentClass> pPC = static_pointer_cast<ParentClass>(pGCC);
+
+ EATEST_VERIFY(pPC == pGCC);
+
+ #if EASTL_RTTI_ENABLED
+ shared_ptr<ChildClass> pCC = dynamic_pointer_cast<ChildClass>(pPC);
+ EATEST_VERIFY(pCC == pGCC);
+ #endif
+
+ #if !defined(__GNUC__) || (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)...
+ eastl::shared_ptr<const void> pVoidPtr = shared_ptr<ParentClass>(new ParentClass);
+ shared_ptr<ParentClass> ap = const_pointer_cast<ParentClass>(static_pointer_cast<const ParentClass>(pVoidPtr));
+ #endif
+
+ //typedef shared_ptr<void const> ASPtr;
+ //shared_ptr<void const> pVoidPtr = ASPtr(new ParentClass);
+ //ASPtr ap = const_pointer_cast<ParentClass>(static_pointer_cast<const ParentClass>(pVoidPtr));
+ }
+
+
+ { // Test static_shared_pointer_cast, etc.
+ shared_ptr<GrandChildClass> pGCC(new GrandChildClass);
+ shared_ptr<ParentClass> pPC = static_shared_pointer_cast<ParentClass /*, EASTLAllocatorType, smart_ptr_deleter<ParentClass>*/ >(pGCC);
+
+ EATEST_VERIFY(pPC == pGCC);
+
+ #if EASTL_RTTI_ENABLED
+ shared_ptr<ChildClass> pCC = dynamic_shared_pointer_cast<ChildClass /*, EASTLAllocatorType, smart_ptr_deleter<ParentClass>*/ >(pPC);
+ EATEST_VERIFY(pCC == pGCC);
+ #endif
+ }
+
+
+ { // Test smart_ptr_deleter
+ shared_ptr<void> pVoid(new ParentClass, smart_ptr_deleter<ParentClass>());
+ EATEST_VERIFY(pVoid.get() != NULL);
+
+ pVoid = shared_ptr<ParentClass>(new ParentClass, smart_ptr_deleter<ParentClass>());
+ EATEST_VERIFY(pVoid.get() != NULL);
+ }
+
+
+ { // Test shared_ptr lambda deleter
+ auto deleter = [](int*) {};
+ eastl::shared_ptr<int> ptr(nullptr, deleter);
+
+ EATEST_VERIFY(!ptr);
+ EATEST_VERIFY(ptr.get() == nullptr);
+ }
+
+
+ { // Test of shared_ptr<void const>
+ #if !defined(__GNUC__) || (__GNUC__ >= 3) // If not using old GCC (GCC 2.x is broken)...
+ shared_ptr<void const> voidPtr = shared_ptr<A1>(new A1);
+ shared_ptr<A1> a1Ptr = const_pointer_cast<A1>(static_pointer_cast<const A1>(voidPtr));
+ #endif
+ }
+
+
+ { // Test of static_pointer_cast
+ shared_ptr<B1> bPtr = shared_ptr<B1>(new B1);
+ shared_ptr<A1> aPtr = static_pointer_cast<A1, B1>(bPtr);
+ }
+
+
+ { // Test shared_ptr<void>
+ {
+ #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<void const, EASTLAllocatorType, smart_ptr_deleter<NamedClass> > voidPtr(pNamedClass0);
+ shared_ptr<void const> 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<void const> voidPtr(pNamedClass0, smart_ptr_deleter<NamedClass>());
+ 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<NamedClass> sp(new NamedClass(pName1));
+ EATEST_VERIFY(!sp == false);
+ EATEST_VERIFY(sp.unique());
+ EATEST_VERIFY(sp->mpName == pName1);
+
+ shared_ptr<NamedClass> 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<A> pWeakA; // leave uninitalized
+ shared_ptr<A> 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<true> throwingAllocator; // Throw on first attempt to allocate.
+ shared_ptr<A> pA0;
+
+ try {
+ A::mCount = 0;
+ pA0 = eastl::allocate_shared<A, ThrowingAllocator<true> >(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<A> pA1(new A('a'), default_delete<A>(), 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 <typename U, typename A, typename D>
+ // shared_ptr(const shared_ptr<U, A, D>& sharedPtr, dynamic_cast_tag);
+ // To do.
+
+ // template <typename U, typename A, typename D, typename UDeleter>
+ // shared_ptr(const shared_ptr<U, A, D>& 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<TestObject>* mpSPTO;
+ eastl::weak_ptr<TestObject>* 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<TestObject> temp(mpWPTO->lock());
+ EATEST_VERIFY(temp->mX == 99);
+
+ eastl::shared_ptr<TestObject> spTO2(*mpSPTO);
+ EATEST_VERIFY(spTO2->mX == 99);
+ EATEST_VERIFY(spTO2.use_count() >= 2);
+
+ eastl::weak_ptr<TestObject> 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<TestObject> spTO(new TestObject(99));
+ weak_ptr<TestObject> 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<TestObject> spTO(new TestObject(55));
+
+ // bool atomic_is_lock_free(const shared_ptr<T>*);
+ EATEST_VERIFY(!atomic_is_lock_free(&spTO));
+
+ // shared_ptr<T> atomic_load(const shared_ptr<T>* pSharedPtr);
+ // shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* pSharedPtr, ... /*std::memory_order memoryOrder*/);
+ shared_ptr<TestObject> spTO2 = atomic_load(&spTO);
+ EATEST_VERIFY(spTO->mX == 55);
+ EATEST_VERIFY(spTO2->mX == 55);
+
+ // void atomic_store(shared_ptr<T>* pSharedPtrA, shared_ptr<T> sharedPtrB);
+ // void atomic_store_explicit(shared_ptr<T>* pSharedPtrA, shared_ptr<T> sharedPtrB, ... /*std::memory_order memoryOrder*/);
+ spTO2->mX = 56;
+ EATEST_VERIFY(spTO->mX == 56);
+ EATEST_VERIFY(spTO2->mX == 56);
+
+ atomic_store(&spTO, shared_ptr<TestObject>(new TestObject(77)));
+ EATEST_VERIFY(spTO->mX == 77);
+ EATEST_VERIFY(spTO2->mX == 56);
+
+ // shared_ptr<T> atomic_exchange(shared_ptr<T>* pSharedPtrA, shared_ptr<T> sharedPtrB);
+ // shared_ptr<T> atomic_exchange_explicit(shared_ptr<T>* pSharedPtrA, shared_ptr<T> 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<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew);
+ // bool atomic_compare_exchange_weak(shared_ptr<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew);
+ // bool atomic_compare_exchange_strong_explicit(shared_ptr<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew, ... /*memory_order memoryOrderSuccess, memory_order memoryOrderFailure*/);
+ // bool atomic_compare_exchange_weak_explicit(shared_ptr<T>* pSharedPtr, shared_ptr<T>* pSharedPtrCondition, shared_ptr<T> sharedPtrNew, ... /*memory_order memoryOrderSuccess, memory_order memoryOrderFailure*/);
+ shared_ptr<TestObject> spTO3 = atomic_load(&spTO2);
+ bool result = atomic_compare_exchange_strong(&spTO3, &spTO, make_shared<TestObject>(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<TestObject>(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<int> pW0;
+ shared_ptr<int> pS0(new int(0));
+ shared_ptr<int> pS1(new int(1));
+ weak_ptr<int> pW1(pS1);
+ weak_ptr<int> pW2;
+ weak_ptr<int> 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<int> pShared2(pW2.lock());
+ shared_ptr<int> 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<NamedClass> wp;
+
+ EATEST_VERIFY(wp.use_count() == 0);
+ EATEST_VERIFY(wp.expired() == true);
+
+ {
+ shared_ptr<NamedClass> 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<const foo> pFoo(new foo);
+ shared_ptr<const foo> 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<const foo> pFoo(new foo);
+ weak_ptr<const foo> 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<foo> pFoo(new foo);
+ weak_ptr<foo> 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<int> 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<int> 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<A> 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<A> 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<A> 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<A> 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<A>(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<int> 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<int> 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<A> 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<A> pT3(new A(2));
+ EATEST_VERIFY(A::mCount == 2);
+
+ linked_ptr<A> 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<A> 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>((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<A> pT2(new A(0));
+ linked_ptr<A> pT3(pT2);
+ pT2.force_delete();
+ pT3.force_delete();
+ }
+
+ EATEST_VERIFY(A::mCount == 0);
+
+
+ { // Verify that subclasses are usable.
+ bool bAlloc = false;
+
+ eastl::linked_ptr<DerivedMockObject> pDMO(new DerivedMockObject(&bAlloc));
+ eastl::linked_ptr<MockObject> a1(pDMO);
+ eastl::linked_ptr<MockObject> a2;
+
+ a2 = pDMO;
+ }
+
+ { // Test regression for a bug.
+ linked_ptr<A> pT2;
+ linked_ptr<A> 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<B> pT4;
+ linked_ptr<A> pT5(pT4);
+ pT5.reset(new A);
+
+ linked_array<A> pT6;
+ linked_array<A> 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<A> pT2(new A[2]);
+ linked_array<A> 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<RefCountTest> ip1;
+ intrusive_ptr<RefCountTest> ip2(NULL, false);
+ intrusive_ptr<RefCountTest> ip3(NULL, true);
+ intrusive_ptr<RefCountTest> ip4(new RefCountTest, true);
+ intrusive_ptr<RefCountTest> ip5(new RefCountTest, false);
+ intrusive_ptr<RefCountTest> ip6(ip1);
+ intrusive_ptr<RefCountTest> 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<RefCountTest> ip1(new RefCountTest);
+ VERIFY(RefCountTest::mCount == 1);
+ VERIFY(ip1->mRefCount == 1);
+ {
+ intrusive_ptr<RefCountTest> 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<RefCountTest> ip1(new RefCountTest);
+ VERIFY(RefCountTest::mCount == 1);
+ VERIFY(ip1->mRefCount == 1);
+ {
+ intrusive_ptr<RefCountTest> 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<RefCountTest> ip1;
+ intrusive_ptr<RefCountTest> 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<RefCountTest> ip1;
+ intrusive_ptr<RefCountTest> ip2(new RefCountTest);
+ intrusive_ptr<RefCountTest> ip3(ip1);
+ intrusive_ptr<RefCountTest> 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<Test> ip;
+ EATEST_VERIFY(ip.get() == NULL);
+
+ ip.reset();
+ EATEST_VERIFY(ip.get() == NULL);
+
+ intrusive_ptr<Test> ip2(NULL, false);
+ EATEST_VERIFY(ip.get() == NULL);
+
+ bool boolValue = false;
+ Test* pTest = new Test(&boolValue);
+ EATEST_VERIFY(boolValue);
+ pTest->AddRef();
+ intrusive_ptr<Test> 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<Test> 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<IntrusiveCustom> bp = intrusive_ptr<IntrusiveCustom>(pIC);
+ intrusive_ptr<IntrusiveCustom> ap = bp;
+ }
+
+ EATEST_VERIFY((IntrusiveCustom::mAddRefCallCount > 0) && (IntrusiveCustom::mReleaseCallCount == IntrusiveCustom::mAddRefCallCount));
+ }
+
+ { // Regression
+ intrusive_ptr<IntrusiveChild> bp = intrusive_ptr<IntrusiveChild>(new IntrusiveChild);
+ intrusive_ptr<IntrusiveParent> 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<RandomLifetimeObject> pSafePtr(pObject);
+
+ eastl::safe_ptr<RandomLifetimeObject> pSafePtrCopy1 = pSafePtr;
+ eastl::safe_ptr<RandomLifetimeObject> pSafePtrCopy2(pSafePtr);
+
+ pSafePtr->DoSomething();
+
+ eastl::safe_ptr<RandomLifetimeObject>* pSafePtrCopy3 = new eastl::safe_ptr<RandomLifetimeObject>(pSafePtr);
+ eastl::safe_ptr<RandomLifetimeObject>* pSafePtrCopy4 = new eastl::safe_ptr<RandomLifetimeObject>(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<const RandomLifetimeObject> pSafePtr(pObject);
+
+ eastl::safe_ptr<const RandomLifetimeObject> pSafePtrCopy1(pSafePtr);
+ eastl::safe_ptr<const RandomLifetimeObject> pSafePtrCopy2 = pSafePtr;
+
+ pSafePtr->DoSomething();
+
+ eastl::safe_ptr<const RandomLifetimeObject>* pSafePtrCopy3 = new eastl::safe_ptr<const RandomLifetimeObject>(pSafePtr);
+ eastl::safe_ptr<const RandomLifetimeObject>* pSafePtrCopy4 = new eastl::safe_ptr<const RandomLifetimeObject>(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
+
+
+
+
+
+
+