diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index 18c51e5..83e5b02 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -26,6 +26,7 @@ void TestTemplates() TestAtomic(); TestScopeHelper(); TestUniquePointer(); + TestSharedPointer(); TestMiscTemplates(); } @@ -1504,7 +1505,7 @@ void TestUniquePointer() { TUniqueRef Temp(new int32); *Temp = 15; - check(*Temp.Get() = 15); + always_check(*Temp.Get() = 15); } FCounter::Num = 0; @@ -1519,45 +1520,45 @@ void TestUniquePointer() TUniqueRef TempB(PtrB); TUniqueRef TempC(PtrC, FDeleter()); - check(TempA == PtrA); - check(TempC != TempB); - check((TempA <=> PtrA) == strong_ordering::equal); - check((TempC <=> TempB) != strong_ordering::equal); + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); int32 TempNum; TempNum = FCounter::Num; TempB.Reset(new FCounter); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; TempB.Reset(new FCounter, FDeleter()); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; FCounter* PtrX = TempB.ReleaseAndReset(new FCounter); - check(FCounter::Num == TempNum + 1); + always_check(FCounter::Num == TempNum + 1); delete PtrX; TempNum = FCounter::Num; FCounter* PtrY = TempB.ReleaseAndReset(new FCounter, FDeleter()); - check(FCounter::Num == TempNum + 1); + always_check(FCounter::Num == TempNum + 1); delete PtrY; - check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); Swap(TempB, TempC); - check(TempC.GetDeleter().Num == 2); + always_check(TempC.GetDeleter().Num == 2); } - check(FCounter::Num == 0); - check(FDeleter::Num == 4); + always_check(FCounter::Num == 0); + always_check(FDeleter::Num == 4); { TUniqueRef Temp(new int32[4]); Temp[0] = 15; - check(Temp.Get()[0] = 15); + always_check(Temp.Get()[0] = 15); } FCounter::Num = 0; @@ -1572,51 +1573,51 @@ void TestUniquePointer() TUniqueRef TempB(PtrB); TUniqueRef TempC(PtrC, FArrayDeleter()); - check(TempA == PtrA); - check(TempC != TempB); - check((TempA <=> PtrA) == strong_ordering::equal); - check((TempC <=> TempB) != strong_ordering::equal); + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); int32 TempNum; TempNum = FCounter::Num; TempB.Reset(new FCounter[4]); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; TempB.Reset(new FCounter[4], FArrayDeleter()); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; FCounter* PtrX = TempB.ReleaseAndReset(new FCounter[4]); - check(FCounter::Num == TempNum + 4); + always_check(FCounter::Num == TempNum + 4); delete [] PtrX; TempNum = FCounter::Num; FCounter* PtrY = TempB.ReleaseAndReset(new FCounter[4], FArrayDeleter()); - check(FCounter::Num == TempNum + 4); + always_check(FCounter::Num == TempNum + 4); delete [] PtrY; - check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); Swap(TempB, TempC); - check(TempC.GetDeleter().Num == 2); + always_check(TempC.GetDeleter().Num == 2); } - check( FCounter::Num == 0); - check(FArrayDeleter::Num == 4); + always_check( FCounter::Num == 0); + always_check(FArrayDeleter::Num == 4); { TUniquePtr Temp = MakeUnique(NoInit); *Temp = 15; - check(*Temp.Get() = 15); + always_check(*Temp.Get() = 15); } { TUniquePtr Temp = MakeUnique(); *Temp = 15; - check(*Temp.Get() = 15); + always_check(*Temp.Get() = 15); } FCounter::Num = 0; @@ -1631,36 +1632,36 @@ void TestUniquePointer() TUniquePtr TempB(PtrB); TUniquePtr TempC(PtrC, FDeleter()); - check(TempA == PtrA); - check(TempC != TempB); - check((TempA <=> PtrA) == strong_ordering::equal); - check((TempC <=> TempB) != strong_ordering::equal); + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); int32 TempNum; TempNum = FCounter::Num; TempB.Reset(new FCounter); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; TempB.Reset(new FCounter, FDeleter()); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; FCounter* PtrX = TempB.ReleaseAndReset(new FCounter); - check(FCounter::Num == TempNum + 1); + always_check(FCounter::Num == TempNum + 1); delete PtrX; TempNum = FCounter::Num; FCounter* PtrY = TempB.ReleaseAndReset(new FCounter, FDeleter()); - check(FCounter::Num == TempNum + 1); + always_check(FCounter::Num == TempNum + 1); delete PtrY; - check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); Swap(TempB, TempC); - check(TempC.GetDeleter().Num == 2); + always_check(TempC.GetDeleter().Num == 2); TUniquePtr TempD(MoveTemp(TempB)); @@ -1669,25 +1670,25 @@ void TestUniquePointer() TempE = nullptr; TempB.Reset(new FCounter); - check(!!TempB); - check(TempB.IsValid()); + always_check(!!TempB); + always_check(TempB.IsValid()); delete TempB.Release(); } - check(FCounter::Num == 0); - check(FDeleter::Num == 4); + always_check(FCounter::Num == 0); + always_check(FDeleter::Num == 4); { TUniquePtr Temp = MakeUnique(4, NoInit); Temp[0] = 15; - check(Temp.Get()[0] = 15); + always_check(Temp.Get()[0] = 15); } { TUniquePtr Temp = MakeUnique(4); Temp[0] = 15; - check(Temp.Get()[0] = 15); + always_check(Temp.Get()[0] = 15); } FCounter::Num = 0; @@ -1702,36 +1703,36 @@ void TestUniquePointer() TUniquePtr TempB(PtrB); TUniquePtr TempC(PtrC, FArrayDeleter()); - check(TempA == PtrA); - check(TempC != TempB); - check((TempA <=> PtrA) == strong_ordering::equal); - check((TempC <=> TempB) != strong_ordering::equal); + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); int32 TempNum; TempNum = FCounter::Num; TempB.Reset(new FCounter[4]); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; TempB.Reset(new FCounter[4], FArrayDeleter()); - check(FCounter::Num == TempNum); + always_check(FCounter::Num == TempNum); TempNum = FCounter::Num; FCounter* PtrX = TempB.ReleaseAndReset(new FCounter[4]); - check(FCounter::Num == TempNum + 4); + always_check(FCounter::Num == TempNum + 4); delete [] PtrX; TempNum = FCounter::Num; FCounter* PtrY = TempB.ReleaseAndReset(new FCounter[4], FArrayDeleter()); - check(FCounter::Num == TempNum + 4); + always_check(FCounter::Num == TempNum + 4); delete [] PtrY; - check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); Swap(TempB, TempC); - check(TempC.GetDeleter().Num == 2); + always_check(TempC.GetDeleter().Num == 2); TUniquePtr TempD(MoveTemp(TempB)); @@ -1740,17 +1741,472 @@ void TestUniquePointer() TempE = nullptr; TempB.Reset(new FCounter[4]); - check(!!TempB); - check(TempB.IsValid()); + always_check(!!TempB); + always_check(TempB.IsValid()); delete [] TempB.Release(); } - check( FCounter::Num == 0); - check(FArrayDeleter::Num == 4); + always_check( FCounter::Num == 0); + always_check(FArrayDeleter::Num == 4); } +void TestSharedPointer() +{ + + FCounter::Num = 0; + FDeleter::Num = 0; + + { + FCounter* PtrA = new FCounter; + FCounter* PtrB = new FCounter; + FCounter* PtrC = new FCounter; + + TSharedRef TempA(PtrA); + TSharedRef TempB(PtrB, FDeleter()); + TSharedRef TempC(PtrC, FDeleter()); + + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); + + int32 TempNum; + + TempNum = FCounter::Num; + TempB.Reset(new FCounter, FDeleter()); + always_check(FCounter::Num == TempNum); + + TempNum = FCounter::Num; + TempC.Reset(new FCounter, FDeleter()); + always_check(FCounter::Num == TempNum); + + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + + Swap(TempB, TempC); + + always_check(TempA.GetDeleter() == nullptr); + always_check(TempC.GetDeleter() != nullptr); + always_check(TempC.GetDeleter()->Num == 2); + + TSharedRef TempD(MoveTemp(TempB)); + } + + always_check(FCounter::Num == 0); + always_check(FDeleter::Num == 4); + + { + TSharedRef Temp = MakeShared(4, NoInit); + Temp[0] = 15; + always_check(Temp.Get()[0] = 15); + } + + { + TSharedRef Temp = MakeShared(4); + Temp[0] = 15; + always_check(Temp.Get()[0] = 15); + } + + FCounter::Num = 0; + FArrayDeleter::Num = 0; + + { + FCounter* PtrA = new FCounter[4]; + FCounter* PtrB = new FCounter[4]; + FCounter* PtrC = new FCounter[4]; + + TSharedRef TempA(PtrA); + TSharedRef TempB(PtrB, FArrayDeleter()); + TSharedRef TempC(PtrC, FArrayDeleter()); + + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); + + int32 TempNum; + + TempNum = FCounter::Num; + TempB.Reset(new FCounter[4], FArrayDeleter()); + always_check(FCounter::Num == TempNum); + + TempNum = FCounter::Num; + TempB.Reset(new FCounter[4], FArrayDeleter()); + always_check(FCounter::Num == TempNum); + + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + + Swap(TempB, TempC); + + always_check(TempA.GetDeleter() == nullptr); + always_check(TempC.GetDeleter() != nullptr); + always_check(TempC.GetDeleter()->Num == 2); + + TSharedRef TempD(MoveTemp(TempB)); + } + + always_check( FCounter::Num == 0); + always_check(FArrayDeleter::Num == 4); + + FCounter::Num = 0; + FDeleter::Num = 0; + + { + FCounter* PtrA = new FCounter; + FCounter* PtrB = new FCounter; + FCounter* PtrC = new FCounter; + + TSharedPtr TempA(PtrA); + TSharedPtr TempB(PtrB, FDeleter()); + TSharedPtr TempC(PtrC, FDeleter()); + + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); + + int32 TempNum; + + TempNum = FCounter::Num; + TempB.Reset(new FCounter, FDeleter()); + always_check(FCounter::Num == TempNum); + + TempNum = FCounter::Num; + TempB.Reset(new FCounter, FDeleter()); + always_check(FCounter::Num == TempNum); + + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + + Swap(TempB, TempC); + + always_check(TempA.GetDeleter() == nullptr); + always_check(TempC.GetDeleter() != nullptr); + always_check(TempC.GetDeleter()->Num == 2); + + TSharedPtr TempD(MoveTemp(TempB)); + + TSharedPtr TempE; + TempE = MoveTemp(TempC); + TempE = nullptr; + + TempB.Reset(new FCounter, FDeleter()); + always_check(!!TempB); + always_check(TempB.IsValid()); + + } + + always_check(FCounter::Num == 0); + always_check(FDeleter::Num == 5); + + { + TSharedPtr Temp = MakeShared(4, NoInit); + Temp[0] = 15; + always_check(Temp.Get()[0] = 15); + } + + { + TSharedPtr Temp = MakeShared(4); + Temp[0] = 15; + always_check(Temp.Get()[0] = 15); + } + + FCounter::Num = 0; + FArrayDeleter::Num = 0; + + { + FCounter* PtrA = new FCounter[4]; + FCounter* PtrB = new FCounter[4]; + FCounter* PtrC = new FCounter[4]; + + TSharedPtr TempA(PtrA); + TSharedPtr TempB(PtrB, FArrayDeleter()); + TSharedPtr TempC(PtrC, FArrayDeleter()); + + always_check(TempA == PtrA); + always_check(TempC != TempB); + always_check((TempA <=> PtrA) == strong_ordering::equal); + always_check((TempC <=> TempB) != strong_ordering::equal); + + int32 TempNum; + + TempNum = FCounter::Num; + TempB.Reset(new FCounter[4], FArrayDeleter()); + always_check(FCounter::Num == TempNum); + + TempNum = FCounter::Num; + TempB.Reset(new FCounter[4], FArrayDeleter()); + always_check(FCounter::Num == TempNum); + + always_check(GetTypeHash(TempB) == GetTypeHash(TempB.Get())); + + Swap(TempB, TempC); + + always_check(TempA.GetDeleter() == nullptr); + always_check(TempC.GetDeleter() != nullptr); + always_check(TempC.GetDeleter()->Num == 2); + + TSharedPtr TempD(MoveTemp(TempB)); + + TSharedPtr TempE; + TempE = MoveTemp(TempC); + TempE = nullptr; + + TempB.Reset(new FCounter[4], FArrayDeleter()); + always_check(!!TempB); + always_check(TempB.IsValid()); + + } + + always_check( FCounter::Num == 0); + always_check(FArrayDeleter::Num == 5); + + { + TSharedPtr Temp; + always_check(!Temp.IsValid()); + if (Temp.Get() == nullptr) { } + } + + { + TSharedPtr Temp(new int32(123)); + + always_check(Temp.IsValid()); + always_check(Temp.IsUnique()); + + const int32 DeferenceTest = *Temp; + + Temp.Reset(); + + always_check(Temp.GetSharedReferenceCount() == 0); + } + + { + TSharedPtr TempA(new bool(false)); + TSharedPtr TempB(TempA); + } + + { + TSharedPtr TempA(new bool(false)); + TSharedPtr TempB; + TempB = TempA; + } + + { + struct FSharedTest { bool bTest; }; + + TSharedPtr TempA(new FSharedTest()); + + TempA->bTest = true; + + (*TempA).bTest = false; + + TSharedPtr TempB(TempA); + + TempA.Reset(); + } + + { + class FBase { bool bTest; }; + + class FDerived : public FBase { }; + + { + TSharedPtr TempA(new FDerived()); + TSharedPtr TempB(StaticCast(TempA)); + } + + { + TSharedPtr TempA(new FDerived()); + TSharedPtr TempB(TempA); + } + + { + TSharedPtr TempA(new FDerived()); + TSharedPtr TempB; + TempB = TempA; + } + } + + { + bool* Ptr = nullptr; + TSharedPtr Temp(Ptr); + always_check(!Temp.IsValid()); + } + + { + TSharedPtr Temp(new bool(true)); + always_check(Temp.IsValid()); + } + + { + TWeakPtr Temp; + always_check(!Temp.Lock().IsValid()); + } + + { + TSharedPtr TempShared(new int32(64)); + TWeakPtr TempWeak(TempShared); + always_check(TempWeak.Lock().IsValid()); + } + + { + TSharedPtr TempShared(new int32(64)); + TWeakPtr TempWeak; + TempWeak = TempShared; + + always_check(TempWeak.Lock().IsValid()); + + TempWeak.Reset(); + always_check(!TempWeak.Lock().IsValid()); + } + + { + TSharedPtr TempShared(new int32(64)); + TWeakPtr TempWeak = TempShared; + TempShared.Reset(); + always_check(!TempWeak.Lock().IsValid()); + } + + { + TSharedPtr TempA(new int32(64)); + TSharedPtr TempB(new int32(21)); + TSharedPtr TempC(TempB); + + always_check(!(TempA == TempB)); + always_check(TempA != TempB); + always_check(TempB == TempC); + } + + { + TSharedPtr TempA(new int32(64)); + TSharedPtr TempB(new int32(21)); + + TWeakPtr WeakA(TempA); + TWeakPtr WeakB(TempB); + TWeakPtr WeakC(TempB); + + always_check(!(WeakA.Lock() == WeakB.Lock())); + always_check(WeakA.Lock() != WeakB.Lock()); + always_check(WeakB.Lock() == WeakC.Lock()); + } + + { + TSharedPtr TempA(new int32(10)); + TSharedPtr TempB(new float32(1.0f)); + TSharedPtr TempC(new float32(2.0f)); + + if (TempB == TempC) { } + + TempB = TempC; + + TSharedPtr TempD(new float32(123.0f)); + + TempB = TempD; + + TWeakPtr TempE = TempB; + TWeakPtr TempF; + + TempF = ConstCast(TempC); + *TempF.Lock() = 20.0f; + } + + { + TSharedPtr Temp; + struct FTest { int32 Value; }; + Temp = TSharedPtr(new FTest()); + Temp->Value = 20; + } + + { + TSharedPtr TempA(nullptr); + TSharedPtr TempB = nullptr; + + TWeakPtr TempD(nullptr); + TWeakPtr TempE = nullptr; + + TempB = TSharedPtr(new float32(0.1f)); + TempB = nullptr; + + TempB = MakeShared(30.0f); + TSharedPtr TempC(MakeShared(2.0)); + + struct FTest + { + TSharedPtr Value; + + TSharedPtr FuncA() { return Value; } + + TSharedPtr FuncB() { return MakeShared(123.0f); } + }; + } + + { + TSharedRef Temp(new float32(123.0f)); + } + + { + TSharedRef Temp(new float32(123.0f)); + const float& RefA = *Temp; + const float& RefB = *Temp.Get(); + } + + { + TSharedRef Temp = MakeShared(123.0f); + } + + { + TSharedRef TempA(new int32(1)); + TSharedPtr TempB(TempA); + } + + { + TSharedPtr TempA(new int32(1)); + TSharedRef TempB(TempA.ToSharedRef()); + } + + { + TSharedRef Temp(new int32(10)); + Temp = TSharedRef(new int32(20)); + } + + { + TSharedRef TempA(new int32(99)); + TWeakPtr TempB = TempA; + always_check(TempB.Lock()); + } + + { + TSharedRef IntRef1(new int32(99)); + TSharedRef IntRef2(new int32(21)); + always_check(!(IntRef1 == IntRef2)); + always_check(IntRef1 != IntRef2); + } + + { + TSharedRef TempA(new int32(21)); + TSharedPtr TempB(TempA); + TSharedPtr TempC; + + always_check(TempA == TempB && TempB == TempA); + always_check(!(TempA != TempB || TempB != TempA)); + always_check(!(TempA == TempC) && (TempA != TempC)); + } + + { + struct FTest : public TSharedFromThis + { + TSharedRef FuncTest() { return AsShared(); } + }; + + TSharedPtr TempA(new FTest()); + + { + FTest* Ptr = TempA.Get(); + TSharedRef TempB(Ptr->FuncTest()); + } + } +} + NAMESPACE_UNNAMED_BEGIN template diff --git a/Redcraft.Utility/Source/Public/Templates/SharedPointer.h b/Redcraft.Utility/Source/Public/Templates/SharedPointer.h new file mode 100644 index 0000000..162dae8 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Templates/SharedPointer.h @@ -0,0 +1,1989 @@ +#pragma once + +#include "CoreTypes.h" +#include "Memory/Memory.h" +#include "Templates/Atomic.h" +#include "Templates/Invoke.h" +#include "Templates/Utility.h" +#include "Memory/MemoryOperator.h" +#include "Templates/Noncopyable.h" +#include "TypeTraits/PrimaryType.h" +#include "Templates/UniquePointer.h" +#include "TypeTraits/Miscellaneous.h" +#include "TypeTraits/TypeProperties.h" +#include "TypeTraits/SupportedOperations.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +template requires (CClass) +class TSharedFromThis; + +template requires (CObject && !CBoundedArray) +class TSharedRef; + +template requires (CObject && !CBoundedArray) +class TSharedPtr; + +template requires (CObject && !CBoundedArray) +class TWeakPtr; + +NAMESPACE_PRIVATE_BEGIN + +// This is the base object for TSharedPtr and uses constructive interference alignment for performance. +class alignas(Memory::ConstructiveInterference) FSharedController : private FSingleton +{ +private: + + using RefCounter = TAtomic; + + // Ensure that counters are lock-free for performance. + static_assert(RefCounter::bIsAlwaysLockFree); + + // When this count is zero the object is destroyed. + // This count is the number of TSharedRef and TSharedPtr. + RefCounter SharedReferenceCount; + + // When this count is zero the controller is destroyed. + // If SharedCounter is not zero this count is one more than the number of TWeakPtr. + RefCounter WeakReferenceCount; + +public: + + // The initialization count is one because TSharedPtr already existed when this controller was constructed. + FORCEINLINE FSharedController() : SharedReferenceCount(1) , WeakReferenceCount(1) { } + + // The controller is a polymorphic class in order to customize the type of erasure of the deleter. + virtual ~FSharedController() { } + + // Destructor object. + virtual void DestroyObject() = 0; + + // Destructor this controller. + virtual void DestroyThis() { delete this; } + + // Get shared reference count, no definite operation order. + FORCEINLINE RefCounter::ValueType GetSharedReferenceCount() + { + // Get the shared reference count as EMemoryOrder::Relaxed, + // since this count is for reference only and has no guarantees, + // where EMemoryOrder::Relaxed only determines the atomicity of this operation and not the order. + return SharedReferenceCount.Load(EMemoryOrder::Relaxed); + } + + // Increases the shared reference count, ensuring that the shared reference count is non-zero before calling. + FORCEINLINE void AddSharedReference() + { + // The check was removed in the release version, so you can use the default EMemoryOrder. + check(SharedReferenceCount.Load() != 0); + + // We assume a non-zero reference count, which can be incremented directly with EMemoryOrder::Relaxed, + // where EMemoryOrder::Relaxed only determines the atomicity of this operation and not the order. + SharedReferenceCount.FetchAdd(1, EMemoryOrder::Relaxed); + } + + // increment the shared reference count, do not need to ensure that the shared reference count is zero, + // if the shared reference count is zero return false. + bool AddSharedReferenceIfUnexpired() + { + RefCounter::ValueType OldSharedReferenceCount = GetSharedReferenceCount(); + + // We need to make sure we don't increase the reference count from zero to one. + while (true) + { + // Never add a shared reference if the pointer has already expired. + if (OldSharedReferenceCount == 0) return false; + + // Attempt to increment the reference count. + // We do a weak read here because we require a loop where the loop only happens in very unusual cases. + if (SharedReferenceCount.CompareExchange(OldSharedReferenceCount, OldSharedReferenceCount + 1, EMemoryOrder::Relaxed, true)) return true; + } + } + + // Release the shared reference count, make sure the shared reference count is not zero before, + // and destroy the object when the shared reference count is released to zero. + void ReleaseSharedReference() + { + // Decrement with EMemoryOrder::Release and get the old value, + // where EMemoryOrder::Release ensures that the side effects of all operations + // on the shared reference count of all threads are visible to this thread, + // preventing the shared reference count from actually going to zero. + RefCounter::ValueType OldSharedReferenceCount = SharedReferenceCount.FetchSub(1, EMemoryOrder::Release); + + // Make sure the shared reference count is not zero before. + check(OldSharedReferenceCount != 0); + + // Destroy the object when the reference count is released to zero. + if (OldSharedReferenceCount == 1) + { + // Use EMemoryOrder::Acquire to ensure visibility of the side effects of the decrement to any other threads. + AtomicThreadFence(EMemoryOrder::Acquire); + + // Destroy objects using the type-erase deleter. + DestroyObject(); + + // Release a weak reference count to indicate that no TSharedRef and TSharedPtr are referencing this controller. + ReleaseWeakReference(); + } + } + + // Increases the weak reference count, ensuring that the weak reference count is non-zero before calling. + FORCEINLINE void AddWeakReference() + { + // The use of EMemoryOrder is the same as in AddSharedReference(). + + check(WeakReferenceCount.Load() != 0); + + WeakReferenceCount.FetchAdd(1, EMemoryOrder::Relaxed); + } + + // Release the weak reference count, make sure the weak reference count is not zero before, + // and destroy the controller when the weak reference count is released to zero. + void ReleaseWeakReference() + { + // The use of EMemoryOrder is the same as in ReleaseSharedReference(). + + RefCounter::ValueType OldWeakReferenceCount = WeakReferenceCount.FetchSub(1, EMemoryOrder::Release); + + check(OldWeakReferenceCount != 0); + + if (OldWeakReferenceCount == 1) + { + AtomicThreadFence(EMemoryOrder::Acquire); + DestroyThis(); // Destroy this controller. + } + } + +}; + +template && !CFinal> +class TSharedControllerWithDeleter; + +template +class TSharedControllerWithDeleter final : public FSharedController, private E +{ +public: + + TSharedControllerWithDeleter() = delete; + + FORCEINLINE TSharedControllerWithDeleter(T* InPtr) : E(), Pointer(InPtr) { } + + template + FORCEINLINE TSharedControllerWithDeleter(T* InPtr, U&& InDeleter) : E(Forward(InDeleter)), Pointer(InPtr) { } + + virtual ~TSharedControllerWithDeleter() = default; + + virtual void DestroyObject() final { Invoke(GetDeleter(), Pointer); } + + FORCEINLINE T* GetPointer() { return Pointer; } + FORCEINLINE E& GetDeleter() { return *this; } + +private: + + // NOTE: NO_UNIQUE_ADDRESS is not valid in MSVC, use base class instead of member variable + //NO_UNIQUE_ADDRESS E Deleter; + + T* Pointer; + +}; + +template +class TSharedControllerWithDeleter final : public FSharedController +{ +public: + + TSharedControllerWithDeleter() = delete; + + FORCEINLINE TSharedControllerWithDeleter(T* InPtr) : Pointer(InPtr), Deleter() { } + + template + FORCEINLINE TSharedControllerWithDeleter(T* InPtr, U&& InDeleter) : Pointer(InPtr), Deleter(Forward(InDeleter)) { } + + virtual ~TSharedControllerWithDeleter() = default; + + virtual void DestroyObject() final { Invoke(GetDeleter(), Pointer); } + + FORCEINLINE T* GetPointer() { return Pointer; } + FORCEINLINE E& GetDeleter() { return Deleter; } + +private: + + T* Pointer; + E Deleter; + +}; + +template +class TSharedControllerWithObject final : public FSharedController +{ +public: + + FORCEINLINE explicit TSharedControllerWithObject(FNoInit) requires (!CConstructibleFrom) { new (&Storage) T; } + + template requires (CConstructibleFrom) + FORCEINLINE explicit TSharedControllerWithObject(Ts&&... Args) { new (&Storage) T(Forward(Args)...); } + + virtual ~TSharedControllerWithObject() = default; + + virtual void DestroyObject() final { GetPointer()->~T(); } + + FORCEINLINE T* GetPointer() const { return reinterpret_cast(&Storage); } + +private: + + mutable TAlignedStorage Storage; + +}; + +template +class TSharedControllerWithArray final : public FSharedController +{ +public: + + static TSharedControllerWithArray* New(size_t N, FNoInit) + { + void* Buffer = Memory::Malloc(sizeof(TSharedControllerWithArray) + sizeof(T) * (N - 1), alignof(TSharedControllerWithArray)); + const auto Controller = new (Buffer) TSharedControllerWithArray(N); + const T* ElementPtr = new (Controller->GetPointer()) T[N]; + check(ElementPtr == Controller->GetPointer()); + return Controller; + } + + static TSharedControllerWithArray* New(size_t N) + { + void* Buffer = Memory::Malloc(sizeof(TSharedControllerWithArray) + sizeof(T) * (N - 1), alignof(TSharedControllerWithArray)); + const auto Controller = new (Buffer) TSharedControllerWithArray(N); + const T* ElementPtr = new (Controller->GetPointer()) T[N](); + check(ElementPtr == Controller->GetPointer()); + return Controller; + } + + virtual ~TSharedControllerWithArray() = default; + + virtual void DestroyObject() final { Memory::Destruct(GetPointer(), Num); } + + virtual void DestroyThis() final + { + this->~TSharedControllerWithArray(); + Memory::Free(this); + } + + FORCEINLINE T* GetPointer() const { return reinterpret_cast(&Storage); } + +private: + + size_t Num; + + mutable TAlignedStorage Storage; + + FORCEINLINE explicit TSharedControllerWithArray(size_t N) : Num(N) { } + +}; + +template +class TSharedProxy : private FSingleton +{ +public: + + FORCEINLINE TSharedProxy(TRemoveExtent* InPtr, FSharedController* InController) + : Pointer(InPtr), Controller(InController) + { } + + NODISCARD FORCEINLINE operator TSharedRef() && { check_code({ return TSharedRef(Pointer, Exchange(Controller, nullptr)); }); return TSharedRef(Pointer, Controller); } + NODISCARD FORCEINLINE operator TSharedPtr() && { check_code({ return TSharedPtr(Pointer, Exchange(Controller, nullptr)); }); return TSharedPtr(Pointer, Controller); } + +# if DO_CHECK + + FORCEINLINE ~TSharedProxy() { checkf(Controller == nullptr, TEXT("The return value from MakeShared() is incorrectly ignored.")); } + +# endif + +private: + + TRemoveExtent* Pointer; + FSharedController* Controller; + +}; + +NAMESPACE_PRIVATE_END + +/** + * Derive your class from TSharedFromThis to enable access to a TSharedRef directly from an object instance + * that's already been allocated. Note that when your class is managed indirectly rather than directly, + * it is NOT enable access to a TSharedRef, for example managed by the array version of the shared pointer. + */ +template requires (CClass) +class TSharedFromThis +{ +public: + + /** + * Provides access to a shared reference to this object. + * Note that is only valid to call this after a shared pointer to the object has already been created. + * Also note that it is illegal to call this in the object's destructor. + */ + FORCEINLINE TSharedRef AsShared() + { + TSharedPtr SharedThis(AsWeak().Lock()); + checkf(SharedThis.Get() == this, TEXT("Your class is now not directly managed. Please check DoesSharedInstanceExist().")); + return MoveTemp(SharedThis).ToSharedRef(); + } + + /** + * Provides access to a shared reference to this object. + * Note that is only valid to call this after a shared pointer to the object has already been created. + * Also note that it is illegal to call this in the object's destructor. + */ + FORCEINLINE TSharedRef AsShared() const + { + TSharedPtr SharedThis(AsWeak().Lock()); + checkf(SharedThis.Get() == this, TEXT("Your class is now not directly managed. Please check DoesSharedInstanceExist().")); + return MoveTemp(SharedThis).ToSharedRef(); + } + + /** Provides access to a weak reference to this object. */ + FORCEINLINE TWeakPtr AsWeak() + { + return WeakThis; + } + + /** Provides access to a weak reference to this object. */ + FORCEINLINE TWeakPtr AsWeak() const + { + return WeakThis; + } + + /** Checks whether our referenced instance is valid, i.e. whether it's safe to call AsShared() or AsWeak(). */ + FORCEINLINE bool DoesSharedInstanceExist() const + { + return !WeakThis.Expired(); + } + +protected: + + FORCEINLINE TSharedFromThis() = default; + FORCEINLINE TSharedFromThis(const TSharedFromThis&) = default; + FORCEINLINE TSharedFromThis(TSharedFromThis&&) = default; + FORCEINLINE TSharedFromThis& operator=(const TSharedFromThis&) = default; + FORCEINLINE TSharedFromThis& operator=(TSharedFromThis&&) = default; + FORCEINLINE ~TSharedFromThis() = default; + +private: + + // Here it is updated by the private constructor of TSharedRef or TSharedPtr. + mutable TWeakPtr WeakThis; + + template requires (CObject && !CBoundedArray) friend class TSharedRef; + template requires (CObject && !CBoundedArray) friend class TSharedPtr; + template requires (CObject && !CBoundedArray) friend class TWeakPtr; + +}; + +/** Shared-ownership non-nullable smart pointer. Use this when you need an object's lifetime to be managed by a shared smart pointer. */ +template requires (CObject && !CBoundedArray) +class TSharedRef final +{ +public: + + using ElementType = T; + using WeakType = TWeakPtr; + + /** TSharedRef cannot be initialized by nullptr. */ + TSharedRef() = delete; + + /** TSharedRef cannot be initialized by nullptr. */ + TSharedRef(nullptr_t) = delete; + + /** Constructs a shared reference that owns the specified object. Must not be nullptr. */ + FORCEINLINE explicit TSharedRef(T* InPtr) : TSharedRef(InPtr, TDefaultDelete()) { } + + /** Constructs a shared reference that owns the specified object with a deleter. Must not be nullptr. */ + template requires (CConstructibleFrom, E> + && CInvocable, TRemoveExtent*> && CDestructible>) + FORCEINLINE TSharedRef(T* InPtr, E&& InDeleter) + : TSharedRef(InPtr, new NAMESPACE_PRIVATE::TSharedControllerWithDeleter>(InPtr, Forward(InDeleter))) + { + checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr.")); + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared object, but pointing to a different object, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The object pointer to use (instead of the incoming shared pointer's object). + */ + template + FORCEINLINE TSharedRef(const TSharedRef& InValue, T* InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr.")); + + Controller->AddSharedReference(); + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared object, but pointing to a different object, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The object pointer to use (instead of the incoming shared pointer's object). + */ + template + FORCEINLINE TSharedRef(TSharedRef&& InValue, T* InPtr): TSharedRef(InValue, InPtr) { } + + /** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */ + FORCEINLINE TSharedRef(const TSharedRef& InValue) : TSharedRef(InValue, InValue.Get()) { } + + /** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedRef(const TSharedRef& InValue) : TSharedRef(InValue, InValue.Get()) { } + + /** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */ + FORCEINLINE TSharedRef(TSharedRef&& InValue) : TSharedRef(InValue) { } + + /** Constructs a TSharedRef which shares ownership of the object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedRef(TSharedRef&& InValue) : TSharedRef(InValue) { } + + /** If this owns an object and it is the last TSharedRef owning it, the object is destroyed through the owned deleter. */ + FORCEINLINE ~TSharedRef() { Controller->ReleaseSharedReference(); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + TSharedRef& operator=(const TSharedRef& InValue) + { + if (Controller == InValue.Controller) + { + Pointer = InValue.Pointer; + return *this; + } + + Controller->ReleaseSharedReference(); + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + Controller->AddSharedReference(); + + return *this; + } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return *this = *reinterpret_cast(&InValue); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) + { + Swap(*this, InValue); + return *this; + } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return *this = MoveTemp(*reinterpret_cast(&InValue)); } + + /** Compares the pointer values of two TSharedRef. */ + NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() == RHS.Get(); } + + /** Compares the pointer values of two TSharedRef. */ + NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() <=> RHS.Get(); } + + /** Compares the pointer values with a raw pointer. */ + NODISCARD FORCEINLINE constexpr bool operator==(T* InPtr) const& { return Get() == InPtr; } + + /** Compares the pointer values with a raw pointer. */ + NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(T* InPtr) const& { return Get() <=> InPtr; } + + /** TSharedRef cannot be initialized by nullptr. */ + void Reset(nullptr_t) = delete; + + /** Replaces the managed object. */ + FORCEINLINE void Reset(T* InPtr) { *this = MoveTemp(TSharedRef(InPtr)); } + + /** TSharedRef cannot be initialized by nullptr. */ + template + void Reset(nullptr_t, E&&) = delete; + + /** Replaces the managed object with a deleter. */ + template requires (CConstructibleFrom, E> + && CInvocable, TRemoveExtent*> && CDestructible>) + FORCEINLINE void Reset(T* InPtr, E&& InDeleter) { *this = MoveTemp(TSharedRef(InPtr, Forward(InDeleter))); } + + /** @return The pointer to the managed object. */ + NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; } + + /** @return The pointer to the owned deleter or nullptr. */ + template requires (CInvocable, TRemoveExtent*> && (CDestructible || CLValueReference)) + NODISCARD FORCEINLINE E* GetDeleter() const + { + const auto ControllerWithDeleter = dynamic_cast*>(Controller); + return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr; + } + + /** @return The a reference or pointer to the object owned by *this, i.e. Get(). */ + NODISCARD FORCEINLINE constexpr T& operator*() const { return *Get(); } + NODISCARD FORCEINLINE constexpr T* operator->() const { return Get(); } + + /** + * Returns the number of shared references to this object (including this reference.) + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return The number of instances managing the current object. + */ + NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller->GetSharedReferenceCount(); } + + /** + * Checks if this is the only instance managing the current object, i.e. whether GetSharedReferenceCount() == 1. + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return true if *this is the only shared_ptr instance managing the current object, false otherwise. + */ + NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; } + + /** + * Checks whether this TSharedRef precedes other in implementation defined owner-based (as opposed to value-based) order. + * This ensures that the ordering of TSharedRef constructed by the aliasing constructor is not affected by object pointer. + * + * @return The ordering of the addresses of the control blocks. + */ + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef& InValue) const { return Controller <=> InValue.Controller; } + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr& InValue) const { return Controller <=> InValue.Controller; } + + /** Overloads the GetTypeHash algorithm for TSharedRef. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedRef& A) { return GetTypeHash(A.Get()); } + + /** Overloads the Swap algorithm for TSharedRef. */ + friend FORCEINLINE constexpr void Swap(TSharedRef& A, TSharedRef& B) + { + Swap(A.Pointer, B.Pointer); + Swap(A.Controller, B.Controller); + } + +private: + + T* Pointer; + NAMESPACE_PRIVATE::FSharedController* Controller; + + template + FORCEINLINE TSharedRef(const TSharedPtr& InValue, T* InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + Controller->AddSharedReference(); + } + + template + FORCEINLINE TSharedRef(TSharedPtr&& InValue, T* InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + InValue.Pointer = nullptr; + InValue.Controller = nullptr; + } + + FORCEINLINE TSharedRef(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController) + : Pointer(InPtr), Controller(InController) + { + check(!((Pointer == nullptr) ^ (Controller == nullptr))); + + if constexpr (CClass) + { + if constexpr (CDerivedFrom>) + { + if (Pointer != nullptr) + { + const TSharedFromThis& SharedFromThis = *Pointer; + checkf(!SharedFromThis.DoesSharedInstanceExist(), TEXT("This object is incorrectly managed by multiple TSharedRef or TSharedPtr.")); + SharedFromThis.WeakThis = *this; + } + } + } + } + + template requires (CObject && !CBoundedArray) friend class TSharedRef; + template requires (CObject && !CBoundedArray) friend class TSharedPtr; + template requires (CObject && !CBoundedArray) friend class TWeakPtr; + + template friend class NAMESPACE_PRIVATE::TSharedProxy; + +}; + +/** Shared-ownership non-nullable smart pointer. Use this when you need an array's lifetime to be managed by a shared smart pointer. */ +template +class TSharedRef final +{ +public: + + using ElementType = T; + using WeakType = TWeakPtr; + + /** TSharedRef cannot be initialized by nullptr. */ + TSharedRef() = delete; + + /** TSharedRef cannot be initialized by nullptr. */ + TSharedRef(nullptr_t) = delete; + + /** Constructs a shared reference that owns the specified array. Must not be nullptr. */ + template requires (CPointer&& CConvertibleTo(*)[], T(*)[]>) + FORCEINLINE explicit TSharedRef(U InPtr) : TSharedRef(InPtr, TDefaultDelete()) { } + + /** Constructs a shared reference that owns the specified array with a deleter. Must not be nullptr. */ + template requires (CConstructibleFrom, E> + && CInvocable, TRemoveExtent*> && CDestructible> + && (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>))) + FORCEINLINE TSharedRef(U InPtr, E&& InDeleter) + : TSharedRef(InPtr, new NAMESPACE_PRIVATE::TSharedControllerWithDeleter>(InPtr, Forward(InDeleter))) + { + checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr.")); + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared array, but pointing to a different array, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The array pointer to use (instead of the incoming shared pointer's array). + */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE TSharedRef(const TSharedRef& InValue, V InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + checkf(InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr. Please use TSharedPtr.")); + + Controller->AddSharedReference(); + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared array, but pointing to a different array, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The array pointer to use (instead of the incoming shared pointer's array). + */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE TSharedRef(TSharedRef&& InValue, V InPtr) : TSharedRef(InValue, InPtr) { } + + /** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */ + FORCEINLINE TSharedRef(const TSharedRef& InValue) : TSharedRef(InValue, InValue.Get()) { } + + /** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedRef(const TSharedRef& InValue) : TSharedRef(InValue, InValue.Get()) { } + + /** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */ + FORCEINLINE TSharedRef(TSharedRef&& InValue) : TSharedRef(InValue) { } + + /** Constructs a TSharedRef which shares ownership of the array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedRef(TSharedRef&& InValue) : TSharedRef(InValue) { } + + /** If this owns an array and it is the last TSharedRef owning it, the array is destroyed through the owned deleter. */ + FORCEINLINE ~TSharedRef() { Controller->ReleaseSharedReference(); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + TSharedRef& operator=(const TSharedRef& InValue) + { + if (Controller == InValue.Controller) + { + Pointer = InValue.Pointer; + return *this; + } + + Controller->ReleaseSharedReference(); + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + Controller->AddSharedReference(); + + return *this; + } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedRef& operator=(const TSharedRef& InValue) { return *this = *reinterpret_cast(&InValue); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) + { + Swap(*this, InValue); + return *this; + } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedRef& operator=(TSharedRef&& InValue) { return *this = MoveTemp(*reinterpret_cast(&InValue)); } + + /** Compares the pointer values of two TSharedRef. */ + NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() == RHS.Get(); } + + /** Compares the pointer values of two TSharedRef. */ + NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedRef& LHS, const TSharedRef& RHS) { return LHS.Get() <=> RHS.Get(); } + + /** Compares the pointer values with a raw pointer. */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + NODISCARD FORCEINLINE constexpr bool operator==(U InPtr) const& { return Get() == InPtr; } + + /** Compares the pointer values with a raw pointer. */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(U InPtr) const& { return Get() <=> InPtr; } + + /** TSharedRef cannot be initialized by nullptr. */ + void Reset(nullptr_t) = delete; + + /** Replaces the managed array. */ + template requires (CPointer && CConvertibleTo(*)[], T(*)[]>) + FORCEINLINE void Reset(U InPtr) { *this = MoveTemp(TSharedRef(InPtr)); } + + /** TSharedRef cannot be initialized by nullptr. */ + template + void Reset(nullptr_t, E&&) = delete; + + /** Replaces the managed array with a deleter. */ + template requires (CPointer && CConvertibleTo(*)[], T(*)[]> + && CConstructibleFrom, E> && CInvocable, TRemoveExtent*> && CDestructible>) + FORCEINLINE void Reset(U InPtr, E&& InDeleter) { *this = MoveTemp(TSharedRef(InPtr, Forward(InDeleter))); } + + /** @return The pointer to the managed array. */ + NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; } + + /** @return The pointer to the owned deleter or nullptr. */ + template requires (CInvocable, TRemoveExtent*> && (CDestructible || CLValueReference)) + NODISCARD FORCEINLINE E* GetDeleter() const + { + const auto ControllerWithDeleter = dynamic_cast*>(Controller); + return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr; + } + + /** @return The element at index, i.e. Get()[Index]. */ + NODISCARD FORCEINLINE constexpr T& operator[](size_t Index) const { return Get()[Index]; } + + /** + * Returns the number of shared references to this array (including this reference.) + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return The number of instances managing the current array. + */ + NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller->GetSharedReferenceCount(); } + + /** + * Checks if this is the only instance managing the current array, i.e. whether GetSharedReferenceCount() == 1. + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return true if *this is the only shared_ptr instance managing the current array, false otherwise. + */ + NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; } + + /** + * Checks whether this TSharedRef precedes other in implementation defined owner-based (as opposed to value-based) order. + * This ensures that the ordering of TSharedRef constructed by the aliasing constructor is not affected by array pointer. + * + * @return The ordering of the addresses of the control blocks. + */ + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef& InValue) const { return Controller <=> InValue.Controller; } + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr& InValue) const { return Controller <=> InValue.Controller; } + + /** Overloads the GetTypeHash algorithm for TSharedRef. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedRef& A) { return GetTypeHash(A.Get()); } + + /** Overloads the Swap algorithm for TSharedRef. */ + friend FORCEINLINE constexpr void Swap(TSharedRef& A, TSharedRef& B) + { + Swap(A.Pointer, B.Pointer); + Swap(A.Controller, B.Controller); + } + +private: + + T* Pointer; + NAMESPACE_PRIVATE::FSharedController* Controller; + + template + FORCEINLINE TSharedRef(const TSharedPtr& InValue, T* InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + Controller->AddSharedReference(); + } + + template + FORCEINLINE TSharedRef(TSharedPtr&& InValue, T* InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + InValue.Pointer = nullptr; + InValue.Controller = nullptr; + } + + FORCEINLINE TSharedRef(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController) + : Pointer(InPtr), Controller(InController) + { + check(!((Pointer == nullptr) ^ (Controller == nullptr))); + } + + template requires (CObject && !CBoundedArray) friend class TSharedRef; + template requires (CObject && !CBoundedArray) friend class TSharedPtr; + template requires (CObject && !CBoundedArray) friend class TWeakPtr; + + template friend class NAMESPACE_PRIVATE::TSharedProxy; + +}; + +/** Shared-ownership smart pointer. Use this when you need an object's lifetime to be managed by a shared smart pointer. */ +template requires (CObject && !CBoundedArray) +class TSharedPtr final +{ +public: + + using ElementType = T; + using WeakType = TWeakPtr; + + /** Constructs an empty shared pointer. */ + FORCEINLINE constexpr TSharedPtr() : Pointer(nullptr), Controller(nullptr) { } + + /** Constructs an empty shared pointer. */ + FORCEINLINE constexpr TSharedPtr(nullptr_t) : TSharedPtr() { } + + /** Constructs a shared pointer that owns the specified object. Note that nullptr is not managed like std. */ + FORCEINLINE explicit TSharedPtr(T* InPtr) : TSharedPtr(InPtr, TDefaultDelete()) { } + + /** Constructs a shared pointer that owns the specified object with a deleter. Note that nullptr is not managed like std. */ + template requires (CConstructibleFrom, E> + && CInvocable, TRemoveExtent*> && CDestructible>) + FORCEINLINE TSharedPtr(T* InPtr, E&& InDeleter) + : TSharedPtr(InPtr, InPtr != nullptr ? new NAMESPACE_PRIVATE::TSharedControllerWithDeleter>(InPtr, Forward(InDeleter)) : nullptr) + { } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared object, but pointing to a different object, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The object pointer to use (instead of the incoming shared pointer's object). + */ + template + FORCEINLINE TSharedPtr(const TSharedPtr& InValue, T* InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr.")); + + if (Controller != nullptr) + { + Controller->AddSharedReference(); + } + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared object, but pointing to a different object, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The object pointer to use (instead of the incoming shared pointer's object). + */ + template + FORCEINLINE TSharedPtr(const TSharedRef& InValue, T* InPtr) + { + new (this) TSharedRef(InValue, InPtr); + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared object, but pointing to a different object, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The object pointer to use (instead of the incoming shared pointer's object). + */ + template + FORCEINLINE TSharedPtr(TSharedPtr&& InValue, T* InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr.")); + + InValue.Pointer = nullptr; + InValue.Controller = nullptr; + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared object, but pointing to a different object, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The object pointer to use (instead of the incoming shared pointer's object). + */ + template + FORCEINLINE TSharedPtr(TSharedRef&& InValue, T* InPtr) + { + new (this) TSharedRef(MoveTemp(InValue), InPtr); + } + + /** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */ + FORCEINLINE TSharedPtr(const TSharedPtr& InValue) : TSharedPtr(InValue, InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr(const TSharedPtr& InValue) : TSharedPtr(InValue, InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr(const TSharedRef& InValue) : TSharedPtr(InValue, InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */ + FORCEINLINE TSharedPtr(TSharedPtr&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr(TSharedPtr&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr(TSharedRef&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { } + + /** Constructs a TSharedPtr which gets ownership of the object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray && (CDestructible || CLValueReference)) + FORCEINLINE TSharedPtr(TUniquePtr&& InValue) : TSharedPtr(InValue.Release(), Forward(InValue.GetDeleter())) { } + + /** If this owns an object and it is the last TSharedPtr owning it, the object is destroyed through the owned deleter. */ + FORCEINLINE ~TSharedPtr() { if (Controller != nullptr) Controller->ReleaseSharedReference(); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + TSharedPtr& operator=(const TSharedPtr& InValue) + { + if (Controller == InValue.Controller) + { + Pointer = InValue.Pointer; + return *this; + } + + if (Controller != nullptr) + { + Controller->ReleaseSharedReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + if (Controller != nullptr) + { + Controller->AddSharedReference(); + } + + return *this; + } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return *this = *reinterpret_cast(&InValue); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr& operator=(const TSharedRef& InValue) { return *reinterpret_cast*>(this) = InValue; } + + /** Replaces the managed object with the one managed by 'InValue'. */ + TSharedPtr& operator=(TSharedPtr&& InValue) + { + if (&InValue == this) UNLIKELY return *this; + + if (Controller != nullptr) + { + Controller->ReleaseSharedReference(); + } + + Pointer = Exchange(InValue.Pointer, nullptr); + Controller = Exchange(InValue.Controller, nullptr); + + return *this; + } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return *this = MoveTemp(*reinterpret_cast(&InValue)); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TSharedPtr& operator=(TSharedRef&& InValue) { return *reinterpret_cast*>(this) = MoveTemp(InValue); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray && (CDestructible || CLValueReference)) + FORCEINLINE TSharedPtr& operator=(TUniquePtr&& InValue) { return Swap(*this, TSharedPtr(MoveTemp(InValue))); } + + /** Effectively the same as calling Reset(). */ + FORCEINLINE TSharedPtr& operator=(nullptr_t) { Reset(); return *this; } + + /** Compares the pointer values of two TSharedPtr. */ + NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() == RHS.Get(); } + + /** Compares the pointer values of two TSharedPtr. */ + NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() <=> RHS.Get(); } + + /** Compares the pointer values with a raw pointer. */ + NODISCARD FORCEINLINE constexpr bool operator==(T* InPtr) const& { return Get() == InPtr; } + + /** Compares the pointer values with a raw pointer. */ + NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(T* InPtr) const& { return Get() <=> InPtr; } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + FORCEINLINE TSharedRef ToSharedRef(T* InPtr) const& + { + checkf(IsValid() && InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr.")); + return TSharedRef(*this, InPtr); + } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + FORCEINLINE TSharedRef ToSharedRef(T* InPtr) && + { + checkf(IsValid() && InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr.")); + return TSharedRef(MoveTemp(*this), InPtr); + } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + FORCEINLINE TSharedRef ToSharedRef() const& { return ToSharedRef(Get()); } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + FORCEINLINE TSharedRef ToSharedRef() && { return ToSharedRef(Get()); } + + /** Replaces the managed object. */ + FORCEINLINE void Reset(T* InPtr = nullptr) { *this = MoveTemp(TSharedPtr(InPtr)); } + + /** Replaces the managed object with a deleter. */ + template requires (CConstructibleFrom, E> + && CInvocable, TRemoveExtent*> && CDestructible>) + FORCEINLINE void Reset(T* InPtr, E&& InDeleter) { *this = MoveTemp(TSharedPtr(InPtr, Forward(InDeleter))); } + + /** @return The pointer to the managed object. */ + NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; } + + /** @return The pointer to the owned deleter or nullptr. */ + template requires (CInvocable, TRemoveExtent*> && (CDestructible || CLValueReference)) + NODISCARD FORCEINLINE E* GetDeleter() const + { + const auto ControllerWithDeleter = dynamic_cast*>(Controller); + return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr; + } + + /** @return true if *this owns an object, false otherwise. */ + NODISCARD FORCEINLINE constexpr bool IsValid() const { return Get() != nullptr; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Get() != nullptr; } + + /** @return The a reference or pointer to the object owned by *this, i.e. Get(). */ + NODISCARD FORCEINLINE constexpr T& operator*() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); } + NODISCARD FORCEINLINE constexpr T* operator->() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get(); } + + /** + * Returns the number of shared references to this object (including this reference.) + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return The number of instances managing the current object. + */ + NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; } + + /** + * Checks if this is the only instance managing the current object, i.e. whether GetSharedReferenceCount() == 1. + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return true if *this is the only shared_ptr instance managing the current object, false otherwise. + */ + NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; } + + /** + * Checks whether this TSharedPtr precedes other in implementation defined owner-based (as opposed to value-based) order. + * This ensures that the ordering of TSharedPtr constructed by the aliasing constructor is not affected by object pointer. + * + * @return The ordering of the addresses of the control blocks. + */ + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef& InValue) const { return Controller <=> InValue.Controller; } + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr& InValue) const { return Controller <=> InValue.Controller; } + + /** Overloads the GetTypeHash algorithm for TSharedPtr. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedPtr& A) { return GetTypeHash(A.Get()); } + + /** Overloads the Swap algorithm for TSharedPtr. */ + friend FORCEINLINE constexpr void Swap(TSharedPtr& A, TSharedPtr& B) + { + Swap(A.Pointer, B.Pointer); + Swap(A.Controller, B.Controller); + } + +private: + + T* Pointer; + NAMESPACE_PRIVATE::FSharedController* Controller; + + FORCEINLINE TSharedPtr(const TWeakPtr& InValue) + { + const bool bIsUnexpired = InValue.Controller != nullptr && InValue.Controller->AddSharedReferenceIfUnexpired(); + + Pointer = bIsUnexpired ? InValue.Pointer : nullptr; + Controller = bIsUnexpired ? InValue.Controller : nullptr; + + } + + FORCEINLINE TSharedPtr(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController) + : Pointer(InPtr), Controller(InController) + { + check(!((Pointer == nullptr) ^ (Controller == nullptr))); + + if constexpr (CClass) + { + if constexpr (CDerivedFrom>) + { + if (Pointer != nullptr) + { + const TSharedFromThis& SharedFromThis = *Pointer; + checkf(!SharedFromThis.DoesSharedInstanceExist(), TEXT("This object is incorrectly managed by multiple TSharedRef or TSharedPtr.")); + SharedFromThis.WeakThis = *this; + } + } + } + } + + template requires (CObject && !CBoundedArray) friend class TSharedRef; + template requires (CObject && !CBoundedArray) friend class TSharedPtr; + template requires (CObject && !CBoundedArray) friend class TWeakPtr; + + template friend class NAMESPACE_PRIVATE::TSharedProxy; + +}; + +/** Shared-ownership smart pointer. Use this when you need an array's lifetime to be managed by a shared smart pointer. */ +template +class TSharedPtr final +{ +public: + + using ElementType = T; + using WeakType = TWeakPtr; + + /** Constructs an empty shared pointer. */ + FORCEINLINE constexpr TSharedPtr() : Pointer(nullptr), Controller(nullptr) { } + + /** Constructs an empty shared pointer. */ + FORCEINLINE constexpr TSharedPtr(nullptr_t) : TSharedPtr() { } + + /** Constructs a shared pointer that owns the specified array. Note that nullptr is not managed like std. */ + template requires (CPointer && CConvertibleTo(*)[], T(*)[]>) + FORCEINLINE explicit TSharedPtr(U InPtr) : TSharedPtr(InPtr, TDefaultDelete()) { } + + /** Constructs a shared pointer that owns the specified array with a deleter. Note that nullptr is not managed like std. */ + template requires (CConstructibleFrom, E> + && CInvocable, TRemoveExtent*> && CDestructible> + && (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>))) + FORCEINLINE TSharedPtr(U InPtr, E&& InDeleter) + : TSharedPtr(InPtr, InPtr != nullptr ? new NAMESPACE_PRIVATE::TSharedControllerWithDeleter>(InPtr, Forward(InDeleter)) : nullptr) + { } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared array, but pointing to a different array, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The array pointer to use (instead of the incoming shared pointer's array). + */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE TSharedPtr(const TSharedPtr& InValue, V InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr.")); + + if (Controller != nullptr) + { + Controller->AddSharedReference(); + } + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared array, but pointing to a different array, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The array pointer to use (instead of the incoming shared pointer's array). + */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE TSharedPtr(const TSharedRef& InValue, V InPtr) + { + new (this) TSharedRef(InValue, InPtr); + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared array, but pointing to a different array, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The array pointer to use (instead of the incoming shared pointer's array). + */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE TSharedPtr(TSharedPtr&& InValue, V InPtr) + : Pointer(InPtr), Controller(InValue.Controller) + { + checkf(!((Pointer == nullptr) ^ (Controller == nullptr)), TEXT("TSharedPtr's aliasing constructor cannot be initialized by nullptr.")); + + InValue.Pointer = nullptr; + InValue.Controller = nullptr; + } + + /** + * Aliasing constructor used to create a shared reference which shares its reference count with + * another shared array, but pointing to a different array, typically a subobject. + * Must not be nullptr. + * + * @param InValue - The shared reference whose reference count should be shared. + * @param InPtr - The array pointer to use (instead of the incoming shared pointer's array). + */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE TSharedPtr(TSharedRef&& InValue, V InPtr) + { + new (this) TSharedRef(MoveTemp(InValue), InPtr); + } + + /** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */ + FORCEINLINE TSharedPtr(const TSharedPtr& InValue) : TSharedPtr(InValue, InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr(const TSharedPtr& InValue) : TSharedPtr(InValue, InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr(const TSharedRef& InValue) : TSharedPtr(InValue, InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */ + FORCEINLINE TSharedPtr(TSharedPtr&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr(TSharedPtr&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { } + + /** Constructs a TSharedPtr which shares ownership of the array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr(TSharedRef&& InValue) : TSharedPtr(MoveTemp(InValue), InValue.Get()) { } + + /** Constructs a TSharedPtr which gets ownership of the array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray && (CDestructible || CLValueReference)) + FORCEINLINE TSharedPtr(TUniquePtr&& InValue) : TSharedPtr(InValue.Release(), Forward(InValue.GetDeleter())) { } + + /** If this owns an array and it is the last TSharedPtr owning it, the array is destroyed through the owned deleter. */ + FORCEINLINE ~TSharedPtr() { if (Controller != nullptr) Controller->ReleaseSharedReference(); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + TSharedPtr& operator=(const TSharedPtr& InValue) + { + if (Controller == InValue.Controller) + { + Pointer = InValue.Pointer; + return *this; + } + + if (Controller != nullptr) + { + Controller->ReleaseSharedReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + if (Controller != nullptr) + { + Controller->AddSharedReference(); + } + + return *this; + } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr& operator=(const TSharedPtr& InValue) { return *this = *reinterpret_cast(&InValue); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr& operator=(const TSharedRef& InValue) { return *reinterpret_cast*>(this) = InValue; } + + /** Replaces the managed array with the one managed by 'InValue'. */ + TSharedPtr& operator=(TSharedPtr&& InValue) + { + if (&InValue == this) UNLIKELY return *this; + + if (Controller != nullptr) + { + Controller->ReleaseSharedReference(); + } + + Pointer = Exchange(InValue.Pointer, nullptr); + Controller = Exchange(InValue.Controller, nullptr); + + return *this; + } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr& operator=(TSharedPtr&& InValue) { return *this = MoveTemp(*reinterpret_cast(&InValue)); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TSharedPtr& operator=(TSharedRef&& InValue) { return *reinterpret_cast*>(this) = MoveTemp(InValue); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray && (CDestructible || CLValueReference)) + FORCEINLINE TSharedPtr& operator=(TUniquePtr&& InValue) { return Swap(*this, TSharedPtr(MoveTemp(InValue))); } + + /** Effectively the same as calling Reset(). */ + FORCEINLINE TSharedPtr& operator=(nullptr_t) { Reset(); return *this; } + + /** Compares the pointer values of two TSharedPtr. */ + NODISCARD friend FORCEINLINE constexpr bool operator==(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() == RHS.Get(); } + + /** Compares the pointer values of two TSharedPtr. */ + NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TSharedPtr& LHS, const TSharedPtr& RHS) { return LHS.Get() <=> RHS.Get(); } + + /** Compares the pointer values with a raw pointer. */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + NODISCARD FORCEINLINE constexpr bool operator==(U InPtr) const& { return Get() == InPtr; } + + /** Compares the pointer values with a raw pointer. */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(U InPtr) const& { return Get() <=> InPtr; } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + template requires (CPointer && CConvertibleTo(*)[], T(*)[]>) + FORCEINLINE TSharedRef ToSharedRef(U InPtr) const& + { + checkf(IsValid() && InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr.")); + return TSharedRef(*this, InPtr); + } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + template requires (CPointer&& CConvertibleTo(*)[], T(*)[]>) + FORCEINLINE TSharedRef ToSharedRef(U InPtr)&& + { + checkf(IsValid() && InPtr != nullptr, TEXT("TSharedRef cannot be initialized by nullptr.")); + return TSharedRef(MoveTemp(*this), InPtr); + } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + FORCEINLINE TSharedRef ToSharedRef() const& { return ToSharedRef(Get()); } + + /** Converts a shared pointer to a shared reference. The pointer MUST be valid or an assertion will trigger. */ + FORCEINLINE TSharedRef ToSharedRef() && { return ToSharedRef(Get()); } + + /** Replaces the managed array. */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE void Reset(U InPtr = nullptr) { *this = MoveTemp(TSharedPtr(InPtr)); } + + /** Replaces the managed array with a deleter. */ + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>) + && CConstructibleFrom, E> && CInvocable, TRemoveExtent*> && CDestructible>) + FORCEINLINE void Reset(U InPtr, E&& InDeleter) { *this = MoveTemp(TSharedPtr(InPtr, Forward(InDeleter))); } + + /** @return The pointer to the managed array. */ + NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; } + + /** @return The pointer to the owned deleter or nullptr. */ + template requires (CInvocable, TRemoveExtent*> && (CDestructible || CLValueReference)) + NODISCARD FORCEINLINE E* GetDeleter() const + { + const auto ControllerWithDeleter = dynamic_cast*>(Controller); + return ControllerWithDeleter != nullptr ? &ControllerWithDeleter->GetDeleter() : nullptr; + } + + /** @return true if *this owns an array, false otherwise. */ + NODISCARD FORCEINLINE constexpr bool IsValid() const { return Get() != nullptr; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Get() != nullptr; } + + /** @return The element at index, i.e. Get()[Index]. */ + NODISCARD FORCEINLINE constexpr T& operator[](size_t Index) const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get()[Index]; } + + /** + * Returns the number of shared references to this array (including this reference.) + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return The number of instances managing the current array. + */ + NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; } + + /** + * Checks if this is the only instance managing the current array, i.e. whether GetSharedReferenceCount() == 1. + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return true if *this is the only shared_ptr instance managing the current array, false otherwise. + */ + NODISCARD FORCEINLINE bool IsUnique() const { return GetSharedReferenceCount() == 1; } + + /** + * Checks whether this TSharedPtr precedes other in implementation defined owner-based (as opposed to value-based) order. + * This ensures that the ordering of TSharedPtr constructed by the aliasing constructor is not affected by array pointer. + * + * @return The ordering of the addresses of the control blocks. + */ + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedRef& InValue) const { return Controller <=> InValue.Controller; } + template NODISCARD FORCEINLINE strong_ordering OwnerCompare(const TSharedPtr& InValue) const { return Controller <=> InValue.Controller; } + + /** Overloads the GetTypeHash algorithm for TSharedPtr. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TSharedPtr& A) { return GetTypeHash(A.Get()); } + + /** Overloads the Swap algorithm for TSharedPtr. */ + friend FORCEINLINE constexpr void Swap(TSharedPtr& A, TSharedPtr& B) + { + Swap(A.Pointer, B.Pointer); + Swap(A.Controller, B.Controller); + } + +private: + + T* Pointer; + NAMESPACE_PRIVATE::FSharedController* Controller; + + FORCEINLINE TSharedPtr(const TWeakPtr& InValue) + { + const bool bIsUnexpired = InValue.Controller != nullptr && InValue.Controller->AddSharedReferenceIfUnexpired(); + + Pointer = bIsUnexpired ? InValue.Pointer : nullptr; + Controller = bIsUnexpired ? InValue.Controller : nullptr; + + } + + FORCEINLINE TSharedPtr(T* InPtr, NAMESPACE_PRIVATE::FSharedController* InController) + : Pointer(InPtr), Controller(InController) + { + check(!((Pointer == nullptr) ^ (Controller == nullptr))); + } + + template requires (CObject && !CBoundedArray) friend class TSharedRef; + template requires (CObject && !CBoundedArray) friend class TSharedPtr; + template requires (CObject && !CBoundedArray) friend class TWeakPtr; + + template friend class NAMESPACE_PRIVATE::TSharedProxy; + +}; + +/** TWeakPtr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by shared pointer. */ +template requires (CObject && !CBoundedArray) +class TWeakPtr final +{ +public: + + using ElementType = T; + + /** Constructs an empty TWeakPtr */ + FORCEINLINE constexpr TWeakPtr() : Pointer(nullptr), Controller(nullptr) { } + + /** Constructs an empty TWeakPtr */ + FORCEINLINE constexpr TWeakPtr(nullptr_t) : TWeakPtr() { } + + /** Constructs new TWeakPtr which shares an object managed by 'InValue'. */ + FORCEINLINE TWeakPtr(const TWeakPtr& InValue) + : Pointer(InValue.Pointer), Controller(InValue.Controller) + { + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + } + + /** Constructs new TWeakPtr which shares an object managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE constexpr TWeakPtr(const TWeakPtr& InValue) : TWeakPtr(*reinterpret_cast(&InValue)) { } + + /** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */ + FORCEINLINE TWeakPtr(TWeakPtr&& InValue) : Pointer(Exchange(InValue.Pointer, nullptr)), Controller(Exchange(InValue.Controller, nullptr)) { } + + /** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE constexpr TWeakPtr(TWeakPtr&& InValue) : TWeakPtr(MoveTemp(*reinterpret_cast(&InValue))) { } + + /** Constructs a weak pointer from a shared reference. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE constexpr TWeakPtr(const TSharedRef& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller) + { + Controller->AddWeakReference(); + } + + /** Constructs a weak pointer from a shared pointer. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE constexpr TWeakPtr(const TSharedPtr& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller) + { + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + } + + /** Destroys the TWeakPtr object. Results in no effect to the managed object. */ + FORCEINLINE ~TWeakPtr() { if (Controller != nullptr) Controller->ReleaseWeakReference(); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + TWeakPtr& operator=(const TWeakPtr& InValue) + { + if (Controller == InValue.Controller) + { + Pointer = InValue.Pointer; + return *this; + } + + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + + return *this; + } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return *this = *reinterpret_cast(&InValue); } + + /** Replaces the managed object with the one managed by 'InValue'. */ + TWeakPtr& operator=(TWeakPtr&& InValue) + { + if (&InValue == this) UNLIKELY return *this; + + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = Exchange(InValue.Pointer, nullptr); + Controller = Exchange(InValue.Controller, nullptr); + + return *this; + } + + /** Replaces the managed object with the one managed by 'InValue'. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return *this = MoveTemp(*reinterpret_cast(&InValue)); } + + /** Assignment operator sets this weak pointer from a shared reference. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TWeakPtr& operator=(const TSharedRef& InValue) + { + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + Controller->AddWeakReference(); + + return *this; + } + + /** Assignment operator sets this weak pointer from a shared pointer. */ + template requires (CConvertibleTo && !CArray) + FORCEINLINE TWeakPtr& operator=(const TSharedPtr& InValue) + { + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + + return *this; + } + + /** Effectively the same as calling Reset(). */ + FORCEINLINE TWeakPtr& operator=(nullptr_t) { Reset(); return *this; } + + /** Resets this weak pointer, removing a weak reference to the object. */ + FORCEINLINE void Reset() + { + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + Pointer = nullptr; + Controller = nullptr; + } + } + + /** + * Returns the number of shared references to this object (Excluding this reference.) + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return The number of instances managing the current object. + */ + NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; } + + /** @return true if the weak pointer is expired and a lock operator would have failed. */ + NODISCARD FORCEINLINE bool Expired() const { return GetSharedReferenceCount() == 0; } + + /** A TSharedPtr which shares ownership of the owned object if Expired() returns false. Else returns nullptr. */ + NODISCARD FORCEINLINE TSharedPtr Lock() const { return *this; } + + /** Overloads the Swap algorithm for TWeakPtr. */ + friend FORCEINLINE constexpr void Swap(TWeakPtr& A, TWeakPtr& B) + { + Swap(A.Pointer, B.Pointer); + Swap(A.Controller, B.Controller); + } + +private: + + T* Pointer; + NAMESPACE_PRIVATE::FSharedController* Controller; + + template requires (CObject && !CBoundedArray) friend class TSharedRef; + template requires (CObject && !CBoundedArray) friend class TSharedPtr; + template requires (CObject && !CBoundedArray) friend class TWeakPtr; + +}; + +/** TWeakPtr is a smart pointer that holds a non-owning ("weak") reference to an array that is managed by shared pointer. */ +template +class TWeakPtr final +{ +public: + + using ElementType = T; + + /** Constructs an empty TWeakPtr */ + FORCEINLINE constexpr TWeakPtr() : Pointer(nullptr), Controller(nullptr) { } + + /** Constructs an empty TWeakPtr */ + FORCEINLINE constexpr TWeakPtr(nullptr_t) : TWeakPtr() { } + + /** Constructs new TWeakPtr which shares an array managed by 'InValue'. */ + FORCEINLINE TWeakPtr(const TWeakPtr& InValue) + : Pointer(InValue.Pointer), Controller(InValue.Controller) + { + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + } + + /** Constructs new TWeakPtr which shares an array managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE constexpr TWeakPtr(const TWeakPtr& InValue) : TWeakPtr(*reinterpret_cast(&InValue)) { } + + /** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */ + FORCEINLINE TWeakPtr(TWeakPtr&& InValue) : Pointer(Exchange(InValue.Pointer, nullptr)), Controller(Exchange(InValue.Controller, nullptr)) { } + + /** Move constructors. Moves a TWeakPtr instance from 'InValue' into this. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE constexpr TWeakPtr(TWeakPtr&& InValue) : TWeakPtr(MoveTemp(*reinterpret_cast(&InValue))) { } + + /** Constructs a weak pointer from a shared reference. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE constexpr TWeakPtr(const TSharedRef& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller) + { + Controller->AddWeakReference(); + } + + /** Constructs a weak pointer from a shared pointer. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE constexpr TWeakPtr(const TSharedPtr& InValue) : Pointer(InValue.Pointer), Controller(InValue.Controller) + { + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + } + + /** Destroys the TWeakPtr array. Results in no effect to the managed array. */ + FORCEINLINE ~TWeakPtr() { if (Controller != nullptr) Controller->ReleaseWeakReference(); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + TWeakPtr& operator=(const TWeakPtr& InValue) + { + if (Controller == InValue.Controller) + { + Pointer = InValue.Pointer; + return *this; + } + + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + + return *this; + } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TWeakPtr& operator=(const TWeakPtr& InValue) { return *this = *reinterpret_cast(&InValue); } + + /** Replaces the managed array with the one managed by 'InValue'. */ + TWeakPtr& operator=(TWeakPtr&& InValue) + { + if (&InValue == this) UNLIKELY return *this; + + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = Exchange(InValue.Pointer, nullptr); + Controller = Exchange(InValue.Controller, nullptr); + + return *this; + } + + /** Replaces the managed array with the one managed by 'InValue'. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TWeakPtr& operator=(TWeakPtr&& InValue) { return *this = MoveTemp(*reinterpret_cast(&InValue)); } + + /** Assignment operator sets this weak pointer from a shared reference. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TWeakPtr& operator=(const TSharedRef& InValue) + { + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + Controller->AddWeakReference(); + + return *this; + } + + /** Assignment operator sets this weak pointer from a shared pointer. */ + template requires (CConvertibleTo && CArray) + FORCEINLINE TWeakPtr& operator=(const TSharedPtr& InValue) + { + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + } + + Pointer = InValue.Pointer; + Controller = InValue.Controller; + + if (Controller != nullptr) + { + Controller->AddWeakReference(); + } + + return *this; + } + + /** Effectively the same as calling Reset(). */ + FORCEINLINE TWeakPtr& operator=(nullptr_t) { Reset(); return *this; } + + /** Resets this weak pointer, removing a weak reference to the array. */ + FORCEINLINE void Reset() + { + if (Controller != nullptr) + { + Controller->ReleaseWeakReference(); + Pointer = nullptr; + Controller = nullptr; + } + } + + /** + * Returns the number of shared references to this array (Excluding this reference.) + * IMPORTANT: With multi-threading this is only an estimate. + * + * @return The number of instances managing the current array. + */ + NODISCARD FORCEINLINE size_t GetSharedReferenceCount() const { return Controller != nullptr ? Controller->GetSharedReferenceCount() : 0; } + + /** @return true if the weak pointer is expired and a lock operator would have failed. */ + NODISCARD FORCEINLINE bool Expired() const { return GetSharedReferenceCount() == 0; } + + /** A TSharedPtr which shares ownership of the owned array if Expired() returns false. Else returns nullptr. */ + NODISCARD FORCEINLINE TSharedPtr Lock() const { return *this; } + + /** Overloads the Swap algorithm for TWeakPtr. */ + friend FORCEINLINE constexpr void Swap(TWeakPtr& A, TWeakPtr& B) + { + Swap(A.Pointer, B.Pointer); + Swap(A.Controller, B.Controller); + } + +private: + + T* Pointer; + NAMESPACE_PRIVATE::FSharedController* Controller; + + template requires (CObject && !CBoundedArray) friend class TSharedRef; + template requires (CObject && !CBoundedArray) friend class TSharedPtr; + template requires (CObject && !CBoundedArray) friend class TWeakPtr; + +}; + +/** Constructs an object of type T and wraps it in a TSharedRef or TSharedPtr. Without initialization. */ +template requires (CObject && !CArray && !CConstructibleFrom && CDestructible) +NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy MakeShared(FNoInit) +{ + const auto Controller = new NAMESPACE_PRIVATE::TSharedControllerWithObject(NoInit); + return NAMESPACE_PRIVATE::TSharedProxy(Controller->GetPointer(), Controller); +} + +/** Constructs an object of type T and wraps it in a TSharedRef or TSharedPtr. */ +template requires (CObject && !CArray && CConstructibleFrom && CDestructible) +NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy MakeShared(Ts&&... Args) +{ + const auto Controller = new NAMESPACE_PRIVATE::TSharedControllerWithObject(Forward(Args)...); + return NAMESPACE_PRIVATE::TSharedProxy(Controller->GetPointer(), Controller); +} + +/** Constructs an array of type T and wraps it in a TSharedRef or TSharedPtr. Without initialization. */ +template requires (CUnboundedArray && CDefaultConstructible> && CDestructible>) +NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy MakeShared(size_t N, FNoInit) +{ + const auto Controller = NAMESPACE_PRIVATE::TSharedControllerWithArray>::New(N, NoInit); + return NAMESPACE_PRIVATE::TSharedProxy(Controller->GetPointer(), Controller); +} + +/** Constructs an array of type T and wraps it in a TSharedRef or TSharedPtr. */ +template requires (CUnboundedArray && CDefaultConstructible> && CDestructible>) +NODISCARD FORCEINLINE NAMESPACE_PRIVATE::TSharedProxy MakeShared(size_t N) +{ + const auto Controller = NAMESPACE_PRIVATE::TSharedControllerWithArray>::New(N); + return NAMESPACE_PRIVATE::TSharedProxy(Controller->GetPointer(), Controller); +} + +/** Construction of arrays of known bound is disallowed. */ +template requires (CBoundedArray) +void MakeShared(Ts&&...) = delete; + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */ +template requires (requires(U* InPtr) { static_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef StaticCast(const TSharedRef& InValue) +{ + return TSharedRef(InValue, static_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */ +template requires (requires(U* InPtr) { static_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef StaticCast(TSharedRef&& InValue) +{ + return TSharedRef(MoveTemp(InValue), static_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */ +template requires (requires(U* InPtr) { dynamic_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef DynamicCast(const TSharedRef& InValue) +{ + return TSharedRef(InValue, dynamic_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */ +template requires (requires(U* InPtr) { dynamic_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef DynamicCast(TSharedRef&& InValue) +{ + return TSharedRef(MoveTemp(InValue), dynamic_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */ +template requires (requires(U* InPtr) { const_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef ConstCast(const TSharedRef& InValue) +{ + return TSharedRef(InValue, const_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */ +template requires (requires(U* InPtr) { const_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef ConstCast(TSharedRef&& InValue) +{ + return TSharedRef(MoveTemp(InValue), const_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */ +template requires (requires(U* InPtr) { reinterpret_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef ReinterpretCast(const TSharedRef& InValue) +{ + return TSharedRef(InValue, reinterpret_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedRef whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */ +template requires (requires(U* InPtr) { reinterpret_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedRef ReinterpretCast(TSharedRef&& InValue) +{ + return TSharedRef(MoveTemp(InValue), reinterpret_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */ +template requires (requires(U* InPtr) { static_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr StaticCast(const TSharedPtr& InValue) +{ + return TSharedPtr(InValue, static_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a static_cast expression. */ +template requires (requires(U* InPtr) { static_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr StaticCast(TSharedPtr&& InValue) +{ + return TSharedPtr(MoveTemp(InValue), static_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */ +template requires (requires(U* InPtr) { dynamic_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr DynamicCast(const TSharedPtr& InValue) +{ + return TSharedPtr(InValue, dynamic_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a dynamic_cast expression. */ +template requires (requires(U* InPtr) { dynamic_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr DynamicCast(TSharedPtr&& InValue) +{ + return TSharedPtr(MoveTemp(InValue), dynamic_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */ +template requires (requires(U* InPtr) { const_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr ConstCast(const TSharedPtr& InValue) +{ + return TSharedPtr(InValue, const_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a const_cast expression. */ +template requires (requires(U* InPtr) { const_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr ConstCast(TSharedPtr&& InValue) +{ + return TSharedPtr(MoveTemp(InValue), const_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */ +template requires (requires(U* InPtr) { reinterpret_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr ReinterpretCast(const TSharedPtr& InValue) +{ + return TSharedPtr(InValue, reinterpret_cast(InValue.Get())); +} + +/** Creates a new instance of TSharedPtr whose stored pointer is obtained from stored pointer of 'InValue' using a reinterpret_cast expression. */ +template requires (requires(U* InPtr) { reinterpret_cast(InPtr); }) +NODISCARD FORCEINLINE TSharedPtr ReinterpretCast(TSharedPtr&& InValue) +{ + return TSharedPtr(MoveTemp(InValue), reinterpret_cast(InValue.Get())); +} + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Templates/Templates.h b/Redcraft.Utility/Source/Public/Templates/Templates.h index 2cb66e8..6efe6d8 100644 --- a/Redcraft.Utility/Source/Public/Templates/Templates.h +++ b/Redcraft.Utility/Source/Public/Templates/Templates.h @@ -16,3 +16,4 @@ #include "Templates/Atomic.h" #include "Templates/ScopeHelper.h" #include "Templates/UniquePointer.h" +#include "Templates/SharedPointer.h" diff --git a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h index ebc96d8..5dc22b6 100644 --- a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h +++ b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h @@ -19,6 +19,7 @@ REDCRAFTUTILITY_API void TestFunction(); REDCRAFTUTILITY_API void TestAtomic(); REDCRAFTUTILITY_API void TestScopeHelper(); REDCRAFTUTILITY_API void TestUniquePointer(); +REDCRAFTUTILITY_API void TestSharedPointer(); REDCRAFTUTILITY_API void TestMiscTemplates(); NAMESPACE_END(Testing)