From 0d99fad3f0e7593457f1d1ef2d2a5da4d8c3adcd Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Fri, 20 Jan 2023 22:40:54 +0800 Subject: [PATCH] feat(memory): add TObserverPtr and the corresponding testing --- .../Source/Private/Testing/MemoryTesting.cpp | 67 ++++++++ .../Source/Public/Memory/ObserverPointer.h | 151 ++++++++++++++++++ .../Source/Public/Testing/MemoryTesting.h | 1 + 3 files changed, 219 insertions(+) create mode 100644 Redcraft.Utility/Source/Public/Memory/ObserverPointer.h diff --git a/Redcraft.Utility/Source/Private/Testing/MemoryTesting.cpp b/Redcraft.Utility/Source/Private/Testing/MemoryTesting.cpp index 8d46f58..eba4618 100644 --- a/Redcraft.Utility/Source/Private/Testing/MemoryTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/MemoryTesting.cpp @@ -6,6 +6,7 @@ #include "Memory/UniquePointer.h" #include "Memory/SharedPointer.h" #include "Memory/MemoryOperator.h" +#include "Memory/ObserverPointer.h" #include "Miscellaneous/AssertionMacros.h" NAMESPACE_REDCRAFT_BEGIN @@ -23,6 +24,7 @@ void TestMemory() TestPointerTraits(); TestUniquePointer(); TestSharedPointer(); + TestObserverPointer(); } void TestAlignment() @@ -1031,6 +1033,71 @@ void TestSharedPointer() } +void TestObserverPointer() +{ + { + int32 IntA; + int32 IntB; + + TObserverPtr TempA; + TObserverPtr TempB = nullptr; + TObserverPtr TempC(&IntA); + TObserverPtr TempD(TempC); + + TempA = TempC; + TempB = MakeObserver(&IntB); + + always_check(TempA == TempC); + always_check(TempB == &IntB); + always_check(TempB.IsValid()); + + always_check(TempA.Release() == &IntA); + always_check(!TempA.IsValid()); + + TempA.Reset(&IntA); + + always_check(TempA == &IntA); + + always_check(GetTypeHash(TempA) == GetTypeHash(&IntA)); + + Swap(TempA, TempB); + + always_check(TempA == &IntB); + always_check(TempB == &IntA); + } + + { + int32 IntA[4]; + int32 IntB[4]; + + TObserverPtr TempA; + TObserverPtr TempB = nullptr; + TObserverPtr TempC(IntA); + TObserverPtr TempD(TempC); + + TempA = TempC; + TempB = MakeObserver(IntB); + + always_check(TempA == TempC); + always_check(TempB == IntB); + always_check(TempB.IsValid()); + + always_check(TempA.Release() == IntA); + always_check(!TempA.IsValid()); + + TempA.Reset(IntA); + + always_check(TempA == IntA); + + always_check(GetTypeHash(TempA) == GetTypeHash(&IntA)); + + Swap(TempA, TempB); + + always_check(TempA == IntB); + always_check(TempB == IntA); + } +} + NAMESPACE_END(Testing) NAMESPACE_MODULE_END(Utility) diff --git a/Redcraft.Utility/Source/Public/Memory/ObserverPointer.h b/Redcraft.Utility/Source/Public/Memory/ObserverPointer.h new file mode 100644 index 0000000..4378dde --- /dev/null +++ b/Redcraft.Utility/Source/Public/Memory/ObserverPointer.h @@ -0,0 +1,151 @@ +#pragma once + +#include "CoreTypes.h" +#include "Templates/Utility.h" +#include "Templates/TypeHash.h" +#include "Memory/PointerTraits.h" +#include "TypeTraits/TypeTraits.h" +#include "Miscellaneous/Compare.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +template requires (CObject && !CBoundedArray) +class TObserverPtr; + +NAMESPACE_PRIVATE_BEGIN + +template struct TIsTObserverPtr : FFalse { }; +template struct TIsTObserverPtr> : FTrue { }; + +NAMESPACE_PRIVATE_END + +template +concept CTObserverPtr = NAMESPACE_PRIVATE::TIsTObserverPtr>::Value; + +template requires (CObject && !CBoundedArray) +class TObserverPtr +{ +public: + + using ElementType = T; + + FORCEINLINE constexpr TObserverPtr() : Pointer(nullptr) { } + + FORCEINLINE constexpr TObserverPtr(nullptr_t) : TObserverPtr() { } + + FORCEINLINE constexpr explicit TObserverPtr(T* InPtr) : Pointer(InPtr) { } + + FORCEINLINE constexpr TObserverPtr(const TObserverPtr&) = default; + FORCEINLINE constexpr TObserverPtr(TObserverPtr&&) = default; + + template requires (CConvertibleTo && !CArray) + FORCEINLINE constexpr TObserverPtr(TObserverPtr InValue) : Pointer(InValue.Pointer) { } + + FORCEINLINE constexpr ~TObserverPtr() = default; + + FORCEINLINE constexpr TObserverPtr& operator=(const TObserverPtr&) = default; + FORCEINLINE constexpr TObserverPtr& operator=(TObserverPtr&&) = default; + + NODISCARD friend FORCEINLINE constexpr bool operator==(const TObserverPtr& LHS, const TObserverPtr& RHS) { return LHS.Get() == RHS.Get(); } + + NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TObserverPtr& LHS, const TObserverPtr& RHS) { return LHS.Get() <=> RHS.Get(); } + + NODISCARD FORCEINLINE constexpr bool operator==(T* InPtr) const& { return Get() == InPtr; } + + NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(T* InPtr) const& { return Get() <=> InPtr; } + + NODISCARD FORCEINLINE constexpr T* Release() { return Exchange(Pointer, nullptr); } + + FORCEINLINE constexpr void Reset(T* InPtr = nullptr) { Pointer = InPtr; } + + NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; } + + NODISCARD FORCEINLINE constexpr bool IsValid() const { return Get() != nullptr; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Get() != nullptr; } + + 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(); } + + NODISCARD FORCEINLINE constexpr operator ElementType*() { return Get(); } + NODISCARD FORCEINLINE constexpr operator const ElementType*() const { return Get(); } + + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TObserverPtr& A) { return GetTypeHash(A.Get()); } + + friend FORCEINLINE constexpr void Swap(TObserverPtr& A, TObserverPtr& B) { Swap(A.Pointer, B.Pointer); } + +private: + + T* Pointer; + +}; + +template +class TObserverPtr +{ +public: + + using ElementType = T; + + FORCEINLINE constexpr TObserverPtr() : Pointer(nullptr) { } + + FORCEINLINE constexpr TObserverPtr(nullptr_t) : TObserverPtr() { } + + template requires (CPointer && CConvertibleTo(*)[], T(*)[]>) + FORCEINLINE constexpr explicit TObserverPtr(U InPtr) : Pointer(InPtr) { } + + FORCEINLINE constexpr TObserverPtr(const TObserverPtr&) = default; + FORCEINLINE constexpr TObserverPtr(TObserverPtr&&) = default; + + template requires (CConvertibleTo(*)[], T(*)[]> && CArray) + FORCEINLINE constexpr TObserverPtr(TObserverPtr InValue) : Pointer(InValue.Pointer) { } + + FORCEINLINE constexpr ~TObserverPtr() = default; + + FORCEINLINE constexpr TObserverPtr& operator=(const TObserverPtr&) = default; + FORCEINLINE constexpr TObserverPtr& operator=(TObserverPtr&&) = default; + + NODISCARD friend FORCEINLINE constexpr bool operator==(const TObserverPtr& LHS, const TObserverPtr& RHS) { return LHS.Get() == RHS.Get(); } + + NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TObserverPtr& LHS, const TObserverPtr& RHS) { return LHS.Get() <=> RHS.Get(); } + + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + NODISCARD FORCEINLINE constexpr bool operator==(U InPtr) const& { return Get() == InPtr; } + + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(U InPtr) const& { return Get() <=> InPtr; } + + NODISCARD FORCEINLINE constexpr T* Release() { return Exchange(Pointer, nullptr); } + + template requires (CNullPointer || (CPointer && CConvertibleTo(*)[], T(*)[]>)) + FORCEINLINE constexpr void Reset(U InPtr = nullptr) { Pointer = InPtr; } + + NODISCARD FORCEINLINE constexpr T* Get() const { return Pointer; } + + NODISCARD FORCEINLINE constexpr bool IsValid() const { return Get() != nullptr; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Get() != nullptr; } + + NODISCARD FORCEINLINE constexpr T& operator[](size_t Index) const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get()[Index]; } + + NODISCARD FORCEINLINE constexpr operator ElementType*() { return Get(); } + NODISCARD FORCEINLINE constexpr operator const ElementType*() const { return Get(); } + + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TObserverPtr& A) { return GetTypeHash(A.Get()); } + + friend FORCEINLINE constexpr void Swap(TObserverPtr& A, TObserverPtr& B) { Swap(A.Pointer, B.Pointer); } + +private: + + T* Pointer; + +}; + +template requires (CObject && !CBoundedArray) +NODISCARD FORCEINLINE constexpr TObserverPtr MakeObserver(TRemoveExtent* InPtr) { return TObserverPtr(InPtr); } + +DEFINE_TPointerTraits(TObserverPtr); + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Testing/MemoryTesting.h b/Redcraft.Utility/Source/Public/Testing/MemoryTesting.h index 07c37b4..344eac9 100644 --- a/Redcraft.Utility/Source/Public/Testing/MemoryTesting.h +++ b/Redcraft.Utility/Source/Public/Testing/MemoryTesting.h @@ -16,6 +16,7 @@ REDCRAFTUTILITY_API void TestMemoryOperator(); REDCRAFTUTILITY_API void TestPointerTraits(); REDCRAFTUTILITY_API void TestUniquePointer(); REDCRAFTUTILITY_API void TestSharedPointer(); +REDCRAFTUTILITY_API void TestObserverPointer(); NAMESPACE_END(Testing)