From 2ef2c4a72955401a364047ef1bb4e49e9a6a8a7d Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Fri, 20 Jan 2023 21:02:28 +0800 Subject: [PATCH] feat(templates): add TPropagateConst and the corresponding testing --- .../Private/Testing/TemplatesTesting.cpp | 41 +++++ .../Source/Public/Templates/PropagateConst.h | 156 ++++++++++++++++++ .../Source/Public/Templates/Templates.h | 1 + .../Source/Public/Testing/TemplatesTesting.h | 1 + 4 files changed, 199 insertions(+) create mode 100644 Redcraft.Utility/Source/Public/Templates/PropagateConst.h diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index e6fc6d6..2c3d8e8 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -25,6 +25,7 @@ void TestTemplates() TestFunction(); TestAtomic(); TestScopeHelper(); + TestPropagateConst(); TestMiscTemplates(); } @@ -1469,6 +1470,46 @@ void TestScopeHelper() } +void TestPropagateConst() +{ + { + struct FTestA + { + void Check(bool bFlag) { check(!bFlag); } + void Check(bool bFlag) const { check(bFlag); } + }; + + struct FTestB + { + FTestB() { Ptr = &Object; } + FTestA Object; + TPropagateConst Ptr; + }; + + FTestB TempA; + const FTestB TempB; + + TempA.Ptr->Check(false); + TempB.Ptr->Check(true); + } + + { + int64 IntA; + int64 IntB; + + TPropagateConst TempA; + TPropagateConst TempB = &IntA; + TPropagateConst TempC = &IntB; + + TempA = TempB; + TempB = TempC; + + check(TempA.IsValid()); + check(TempA == &IntA); + check(TempB == TempC); + } +} + NAMESPACE_UNNAMED_BEGIN template diff --git a/Redcraft.Utility/Source/Public/Templates/PropagateConst.h b/Redcraft.Utility/Source/Public/Templates/PropagateConst.h new file mode 100644 index 0000000..9b88039 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Templates/PropagateConst.h @@ -0,0 +1,156 @@ +#pragma once + +#include "CoreTypes.h" +#include "Templates/Utility.h" +#include "Templates/TypeHash.h" +#include "Memory/PointerTraits.h" +#include "TypeTraits/TypeTraits.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +template requires (TPointerTraits::bIsPointer) +class TPropagateConst; + +NAMESPACE_PRIVATE_BEGIN + +template struct TIsTPropagateConst : FFalse { }; +template struct TIsTPropagateConst> : FTrue { }; + +NAMESPACE_PRIVATE_END + +template +concept CTPropagateConst = NAMESPACE_PRIVATE::TIsTPropagateConst>::Value; + +/** + * TPropagateConst is a const-propagating wrapper for pointers and pointer-like objects. + * It treats the wrapped pointer as a pointer to const when accessed through a const access path, hence the name. + */ +template requires (TPointerTraits::bIsPointer) +class TPropagateConst final +{ +public: + + using ElementType = TPointerTraits::ElementType; + + /** Constructs an TPropagateConst, default-initializing underlying pointer. */ + FORCEINLINE constexpr TPropagateConst() = default; + + /** Initializes underlying pointer if by direct-non-list-initialization with the expression Forward(InValue). */ + template requires (CConstructibleFrom && !CTPropagateConst>) + FORCEINLINE constexpr explicit (!CConvertibleTo) TPropagateConst(U&& InValue) + : Ptr(Forward(InValue)) + { } + + /** Explicitly defaulted copy/move constructor that copy/move constructs underlying pointer. */ + FORCEINLINE constexpr TPropagateConst(const TPropagateConst&) = default; + FORCEINLINE constexpr TPropagateConst(TPropagateConst&&) = default; + + /** Initializes underlying pointer as if by direct-non-list-initialization. */ + template requires (CConstructibleFrom) + FORCEINLINE constexpr explicit (!CConvertibleTo) TPropagateConst(const TPropagateConst& InValue) + : Ptr(InValue.Ptr) + { } + + /** Initializes underlying pointer as if by direct-non-list-initialization. */ + template requires (CConstructibleFrom) + FORCEINLINE constexpr explicit (!CConvertibleTo) TPropagateConst(TPropagateConst&& InValue) + : Ptr(MoveTemp(InValue.Ptr)) + { } + + /** Destructs an TPropagateConst, destroying the contained underlying pointer */ + FORCEINLINE constexpr ~TPropagateConst() = default; + + /** Explicitly defaulted copy/move assignment operator that copy/move assigns underlying pointer. */ + FORCEINLINE constexpr TPropagateConst& operator=(const TPropagateConst& InValue) = default; + FORCEINLINE constexpr TPropagateConst& operator=(TPropagateConst&& InValue) = default; + + /** Assigns underlying pointer from 'InValue'. */ + template requires (CAssignableFrom) + FORCEINLINE constexpr TPropagateConst& operator=(const TPropagateConst& InValue) + { + Ptr = InValue.Ptr; + return *this; + } + + /** Assigns underlying pointer from 'InValue'. */ + template requires (CAssignableFrom) + FORCEINLINE constexpr TPropagateConst& operator=(TPropagateConst&& InValue) + { + Ptr = MoveTemp(InValue.Ptr); + return *this; + } + + /** Assigns underlying pointer from 'InValue'. */ + template requires (CConvertibleTo) + FORCEINLINE constexpr TPropagateConst& operator=(U&& InValue) + { + Ptr = Forward(InValue); + return *this; + } + + /** Compares the pointer values of two TPropagateConst. */ + NODISCARD friend FORCEINLINE constexpr bool operator==(const TPropagateConst& LHS, const TPropagateConst& RHS) requires (CWeaklyEqualityComparable) { return LHS.Ptr == RHS.Ptr; } + + /** Compares the pointer values of two TPropagateConst. */ + NODISCARD friend FORCEINLINE constexpr decltype(auto) operator<=>(const TPropagateConst& LHS, const TPropagateConst& RHS) requires (CSynthThreeWayComparable) { return SynthThreeWayCompare(LHS.Ptr, RHS.Ptr); } + + /** Compares the pointer values with a underlying pointer. */ + template requires (CWeaklyEqualityComparable) + NODISCARD FORCEINLINE constexpr bool operator==(U InPtr) const& { return Ptr == InPtr; } + + /** Compares the pointer values with a underlying pointer. */ + template requires (CSynthThreeWayComparable) + NODISCARD FORCEINLINE constexpr decltype(auto) operator<=>(U InPtr) const& { return SynthThreeWayCompare(Ptr, InPtr); } + + /** @return The pointer to the object pointed to by the wrapped pointer. */ + NODISCARD FORCEINLINE constexpr ElementType* Get() { return TPointerTraits::ToAddress(Ptr); } + NODISCARD FORCEINLINE constexpr const ElementType* Get() const { return TPointerTraits::ToAddress(Ptr); } + + /** @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 ElementType& operator*() { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); } + NODISCARD FORCEINLINE constexpr const ElementType& operator*() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return *Get(); } + NODISCARD FORCEINLINE constexpr ElementType* operator->() { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get(); } + NODISCARD FORCEINLINE constexpr const ElementType* operator->() const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get(); } + + /** @return The element at index, i.e. Get()[Index]. */ + NODISCARD FORCEINLINE constexpr T& operator[](size_t Index) { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get()[Index]; } + NODISCARD FORCEINLINE constexpr const T& operator[](size_t Index) const { checkf(IsValid(), TEXT("Read access violation. Please check IsValid().")); return Get()[Index]; } + + /** @return The pointer to the object pointed to by the wrapped pointer-like object. */ + NODISCARD FORCEINLINE constexpr operator ElementType*() requires (CConvertibleTo) { return Ptr; } + NODISCARD FORCEINLINE constexpr operator const ElementType*() const requires (CConvertibleTo) { return Ptr; } + + /** @return The reference to the pointer-like object stored. */ + NODISCARD FORCEINLINE constexpr T& GetUnderlying() { return Ptr; } + NODISCARD FORCEINLINE constexpr const T& GetUnderlying() const { return Ptr; } + + /** Overloads the GetTypeHash algorithm for TPropagateConst. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TPropagateConst& A) requires (CHashable) + { + return GetTypeHash(A.Ptr); + } + + /** Overloads the Swap algorithm for TPropagateConst. */ + friend FORCEINLINE constexpr void Swap(TPropagateConst& A, TPropagateConst& B) requires (CSwappable) + { + Swap(A.Ptr, B.Ptr); + } + +private: + + T Ptr; + +}; + +template +TPropagateConst(T) -> TPropagateConst; + +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 7e3c0aa..5039e9f 100644 --- a/Redcraft.Utility/Source/Public/Templates/Templates.h +++ b/Redcraft.Utility/Source/Public/Templates/Templates.h @@ -15,3 +15,4 @@ #include "Templates/Function.h" #include "Templates/Atomic.h" #include "Templates/ScopeHelper.h" +#include "Templates/PropagateConst.h" diff --git a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h index 3d92d0f..7754f08 100644 --- a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h +++ b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h @@ -18,6 +18,7 @@ REDCRAFTUTILITY_API void TestTuple(); REDCRAFTUTILITY_API void TestFunction(); REDCRAFTUTILITY_API void TestAtomic(); REDCRAFTUTILITY_API void TestScopeHelper(); +REDCRAFTUTILITY_API void TestPropagateConst(); REDCRAFTUTILITY_API void TestMiscTemplates(); NAMESPACE_END(Testing)