diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index 0ae8927..8a007c6 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -940,7 +940,8 @@ void TestTuple() always_check(C == 'A'); } }; - MakeTuple(1, 1.2f, 'A').Construct(); + + Ignore = MakeTuple(1, 1.2f, 'A').Construct(); } { diff --git a/Redcraft.Utility/Source/Public/Memory/Alignment.h b/Redcraft.Utility/Source/Public/Memory/Alignment.h index c24767a..210f589 100644 --- a/Redcraft.Utility/Source/Public/Memory/Alignment.h +++ b/Redcraft.Utility/Source/Public/Memory/Alignment.h @@ -10,8 +10,17 @@ NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_BEGIN(Memory) +/** Check if an alignment is an integer power of 2. */ FORCEINLINE constexpr bool IsValidAlignment(size_t Alignment) { return !(Alignment & (Alignment - 1)); } +/** + * Aligns a value to the nearest higher multiple of 'Alignment', which must be a power of 2. + * + * @param InValue - The value to align. + * @param Alignment - The alignment value, must be a power of 2. + * + * @return The value aligned up to the specified alignment. + */ template requires (CIntegral || CPointer) FORCEINLINE constexpr T Align(T InValue, size_t Alignment) { @@ -19,6 +28,14 @@ FORCEINLINE constexpr T Align(T InValue, size_t Alignment) return (T)(((uint64)(InValue) + static_cast(Alignment) - 1) & ~(static_cast(Alignment) - 1)); } +/** + * Aligns a value to the nearest lower multiple of 'Alignment', which must be a power of 2. + * + * @param InValue - The value to align. + * @param Alignment - The alignment value, must be a power of 2. + * + * @return The value aligned down to the specified alignment. + */ template requires (CIntegral || CPointer) FORCEINLINE constexpr T AlignDown(T InValue, size_t Alignment) { @@ -26,13 +43,28 @@ FORCEINLINE constexpr T AlignDown(T InValue, size_t Alignment) return (T)((uint64)(InValue) & ~(static_cast(Alignment) - 1)); } +/** + * Aligns a value to the nearest higher multiple of 'Alignment'. + * + * @param InValue - The value to align. + * @param Alignment - The alignment value, can be any arbitrary value. + * + * @return The value aligned up to the specified alignment. + */ template requires (CIntegral || CPointer) FORCEINLINE constexpr T AlignArbitrary(T InValue, size_t Alignment) { - checkf(IsValidAlignment(Alignment), TEXT("The alignment value must be an integer power of 2.")); return (T)((((uint64)(InValue) + static_cast(Alignment) - 1) / static_cast(Alignment)) * static_cast(Alignment)); } +/** + * Checks if a pointer is aligned to the specified alignment. + * + * @param InValue - The value to align. + * @param Alignment - The alignment value, must be a power of 2. + * + * @return true if the pointer is aligned to the specified alignment, false otherwise. + */ template requires (CIntegral || CPointer) FORCEINLINE constexpr bool IsAligned(T InValue, size_t Alignment) { diff --git a/Redcraft.Utility/Source/Public/Memory/Memory.h b/Redcraft.Utility/Source/Public/Memory/Memory.h index 784c062..e4d5cff 100644 --- a/Redcraft.Utility/Source/Public/Memory/Memory.h +++ b/Redcraft.Utility/Source/Public/Memory/Memory.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "CoreTypes.h" #include "TypeTraits/TypeTraits.h" @@ -13,100 +13,280 @@ NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_BEGIN(Memory) +/** + * Default allocator alignment. + * Blocks >= 16 bytes will be 16-byte-aligned, Blocks < 16 will be 8-byte aligned. If the allocator does + * not support allocation alignment, the alignment will be ignored. + */ inline constexpr size_t DefaultAlignment = 0; + +/** + * Minimum allocator alignment. + */ inline constexpr size_t MinimumAlignment = 8; #ifdef __cpp_lib_hardware_interference_size -inline constexpr size_t DestructiveInterference = std::hardware_destructive_interference_size; +/** + * Minimum offset between two objects to avoid false sharing. + * + * struct FTwoCacheLiner { // occupies two cache lines + * alignas(DestructiveInterference) TAtomic X; + * alignas(DestructiveInterference) TAtomic Y; + * }; + * + */ +inline constexpr size_t DestructiveInterference = std::hardware_destructive_interference_size; + +/** + * Maximum size of contiguous memory to promote true sharing. + * + * struct alignas(ConstructiveInterference) FOneCacheLiner { // occupies one cache line + * TAtomic X; + * TAtomic Y; + * }; + * + */ inline constexpr size_t ConstructiveInterference = std::hardware_constructive_interference_size; #else +/** + * Minimum offset between two objects to avoid false sharing. + * + * struct FTwoCacheLiner { // occupies two cache lines + * alignas(DestructiveInterference) TAtomic X; + * alignas(DestructiveInterference) TAtomic Y; + * }; + * + */ inline constexpr size_t DestructiveInterference = 64; + +/** + * Maximum size of contiguous memory to promote true sharing. + * + * struct alignas(ConstructiveInterference) FOneCacheLiner { // occupies one cache line + * TAtomic X; + * TAtomic Y; + * }; + * + */ inline constexpr size_t ConstructiveInterference = 64; #endif +/** + * Copies 'Count' bytes from the buffer pointed to by 'Source' to the buffer pointed to by 'Destination'. + * The buffers may overlap, copying takes place as if the characters were copied to a temporary character + * array and then the characters were copied from the array to 'Destination'. + * + * @param Destination - The pointer to the memory location to copy to. + * @param Source - The pointer to the memory location to copy from. + * @param Count - The number of bytes to copy. + * + * @return Destination + */ FORCEINLINE void* Memmove(void* Destination, const void* Source, size_t Count) { return std::memmove(Destination, Source, Count); } +/** + * Reinterprets the buffers pointed to by 'BufferLHS' and 'BufferRHS' as arrays of unsigned char and + * compares the first 'Count' characters of these arrays. The comparison is done lexicographically. + * The sign of the result is the sign of the difference between the values of the first pair of bytes + * (both interpreted as unsigned char) that differ in the objects being compared. + * + * @param BufferLHS - The pointers to the memory buffers to compare. + * @param BufferRHS - The pointers to the memory buffers to compare. + * @param Count - The number of bytes to examine. + * + * @return Negative value if the first differing byte in 'BufferLHS' is less than the corresponding byte in 'BufferRHS'. + * 0 if all 'Count' bytes of 'BufferLHS' and 'BufferRHS' are equal. + * Positive value if the first differing byte in 'BufferLHS' is greater than the corresponding byte in 'BufferRHS'. + */ FORCEINLINE int32 Memcmp(const void* BufferLHS, const void* BufferRHS, size_t Count) { return std::memcmp(BufferLHS, BufferRHS, Count); } -FORCEINLINE void Memset(void* Destination, uint8 ValueToSet, size_t Count) +/** + * Copies 'ValueToSet' into each of the first 'Count' characters of the buffer pointed to by 'Destination'. + * + * @param Destination - The pointer to the buffer to fill. + * @param ValueToSet - The fill byte. + * @param Count - The number of bytes to fill. + * + * @return Destination + */ +FORCEINLINE void* Memset(void* Destination, uint8 ValueToSet, size_t Count) { - std::memset(Destination, ValueToSet, Count); + return std::memset(Destination, ValueToSet, Count); } +/** + * Copies 0 into each of the first 'Count' characters of the buffer pointed to by 'Destination'. + * + * @param Destination - The pointer to the buffer to fill. + * @param Count - The number of bytes to fill. + * + * @return Destination + */ FORCEINLINE void* Memzero(void* Destination, size_t Count) { return std::memset(Destination, 0, Count); } +/** + * Copies 'Count' bytes from the buffer pointed to by 'Source' to the buffer pointed to by 'Destination'. + * If the buffers overlap, the behavior is undefined. + * + * @param Destination - The pointer to the memory location to copy to. + * @param Source - The pointer to the memory location to copy from. + * @param Count - The number of bytes to copy. + * + * @return Destination + */ FORCEINLINE void* Memcpy(void* Destination, const void* Source, size_t Count) { return std::memcpy(Destination, Source, Count); } -template +/** + * Copies the object referenced to by 'Source' to the object referenced to by 'Destination'. + * The objects may overlap, copying takes place as if the characters were copied to a temporary character + * array and then the characters were copied from the array to 'Destination'. + * + * @param Destination - The reference to the object to copy to. + * @param Source - The reference to the object to copy from. + */ +template requires (!CPointer) FORCEINLINE void Memmove(T& Destination, const T& Source) { - static_assert(!CPointer, "For pointers use the three parameters function"); Memmove(&Destination, &Source, sizeof(T)); } -template +/** + * Reinterprets the objects referenced to by 'BufferLHS' and 'BufferRHS' as arrays of unsigned char and + * compares the all characters of these arrays. The comparison is done lexicographically. + * The sign of the result is the sign of the difference between the values of the first pair of bytes + * (both interpreted as unsigned char) that differ in the objects being compared. + * + * @param BufferLHS - The reference to the object to compare. + * @param BufferRHS - The reference to the object to compare. + * + * @return Negative value if the first differing byte in 'BufferLHS' is less than the corresponding byte in 'BufferRHS'. + * 0 if all bytes of 'BufferLHS' and 'BufferRHS' are equal. + * Positive value if the first differing byte in 'BufferLHS' is greater than the corresponding byte in 'BufferRHS'. + */ +template requires (!CPointer) FORCEINLINE int32 Memcmp(const T& BufferLHS, const T& BufferRHS) { - static_assert(!CPointer, "For pointers use the three parameters function"); return Memcmp(&BufferLHS, &BufferRHS, sizeof(T)); } -template -FORCEINLINE void Memset(T& Source, uint8 ValueToSet) +/** + * Copies 'ValueToSet' into each of the all characters of the object referenced to by 'Destination'. + * + * @param Destination - The reference to the object to fill. + * @param ValueToSet - The fill byte. + */ +template requires (!CPointer) +FORCEINLINE void Memset(T& Destination, uint8 ValueToSet) { - static_assert(!CPointer, "For pointers use the three parameters function"); - Memset(&Source, ValueToSet, sizeof(T)); + Memset(&Destination, ValueToSet, sizeof(T)); } -template -FORCEINLINE void Memzero(T& Source) +/** + * Copies 0 into each of the all characters of the object referenced to by 'Destination'. + * + * @param Destination - The reference to the object to fill. + * @param ValueToSet - The fill byte. + */ +template requires (!CPointer) +FORCEINLINE void Memzero(T& Destination) { - static_assert(!CPointer, "For pointers use the two parameters function"); - Memzero(&Source, sizeof(T)); + Memzero(&Destination, sizeof(T)); } -template +/** + * Copies the object referenced to by 'Source' to the object referenced to by 'Destination'. + * If the objects overlap, the behavior is undefined. + * + * @param Destination - The reference to the object to copy to. + * @param Source - The reference to the object to copy from. + */ +template requires (!CPointer) FORCEINLINE void Memcpy(T& Destination, const T& Source) { - static_assert(!CPointer, "For pointers use the three parameters function"); Memcpy(&Destination, &Source, sizeof(T)); } -FORCEINLINE void* SystemMalloc(size_t Count) +/** Fallback to std::malloc(). */ +NODISCARD FORCEINLINE void* SystemMalloc(size_t Count) { return std::malloc(Count); } -FORCEINLINE void* SystemRealloc(void* Ptr, size_t Count) +/** Fallback to std::realloc(). */ +NODISCARD FORCEINLINE void* SystemRealloc(void* Ptr, size_t Count) { return std::realloc(Ptr, Count); } +/** Fallback to std::free(). */ FORCEINLINE void SystemFree(void* Ptr) { std::free(Ptr); } -REDCRAFTUTILITY_API void* Malloc(size_t Count, size_t Alignment = DefaultAlignment); -REDCRAFTUTILITY_API void* Realloc(void* Ptr, size_t Count, size_t Alignment = DefaultAlignment); +/** + * Allocates 'Count' bytes of uninitialized storage with 'Alignment'. + * + * @param Count - The number of bytes to allocate. + * @param Alignment - The alignment value, must be a power of 2. + * + * @return The non-null pointer to the beginning of newly allocated memory. To avoid a memory leak, + * the returned pointer must be deallocated with Free() or Realloc(). + * + * @see DefaultAlignment + */ +NODISCARD REDCRAFTUTILITY_API void* Malloc(size_t Count, size_t Alignment = DefaultAlignment); + +/** + * Reallocates the given area of memory. It must be previously allocated by Malloc() or Realloc(). + * + * @param Ptr - The pointer to the memory area to be reallocated. + * @param Count - The number of bytes to allocate. + * @param Alignment - The alignment value, must be a power of 2. + * + * @return The non-null pointer to the beginning of newly allocated memory. To avoid a memory leak, + * the returned pointer must be deallocated with Free() or Realloc(). + * + * @see DefaultAlignment + */ +NODISCARD REDCRAFTUTILITY_API void* Realloc(void* Ptr, size_t Count, size_t Alignment = DefaultAlignment); + +/** + * Deallocates the space previously allocated by Malloc() or Realloc(). + * If 'Ptr' is a nullptr, the function does nothing. + * + * @param Ptr - The pointer to the memory to deallocate. + */ REDCRAFTUTILITY_API void Free(void* Ptr); -REDCRAFTUTILITY_API size_t QuantizeSize(size_t Count, size_t Alignment = DefaultAlignment); + +/** + * For some allocators this will return the actual size that should be requested to eliminate + * internal fragmentation. The return value will always be >= 'Count'. This can be used to grow + * and shrink containers to optimal sizes. + * This call is always fast and threadsafe with no locking. + * + * @param Count - The number of bytes to allocate. + * @param Alignment - The alignment value, must be a power of 2. + * + * @return The optimized new size. + */ +NODISCARD REDCRAFTUTILITY_API size_t QuantizeSize(size_t Count, size_t Alignment = DefaultAlignment); NAMESPACE_END(Memory) @@ -117,8 +297,8 @@ NAMESPACE_REDCRAFT_END #pragma warning(disable : 28251) // The global overload operators new/delete do not cross .dll boundaries, and the macros should be placed in the .cpp of each module. -#define REPLACEMENT_OPERATOR_NEW_AND_DELETE \ - void* operator new(std::size_t Count) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count); } \ - void* operator new(std::size_t Count, std::align_val_t Alignment) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count, static_cast(Alignment)); } \ - void operator delete(void* Ptr) noexcept { NAMESPACE_REDCRAFT::Memory::Free(Ptr); } \ - void operator delete(void* Ptr, std::align_val_t Alignment) noexcept { NAMESPACE_REDCRAFT::Memory::Free(Ptr); } +#define REPLACEMENT_OPERATOR_NEW_AND_DELETE \ + NODISCARD void* operator new(std::size_t Count) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count); } \ + NODISCARD void* operator new(std::size_t Count, std::align_val_t Alignment) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count, static_cast(Alignment)); } \ + void operator delete(void* Ptr) noexcept { NAMESPACE_REDCRAFT::Memory::Free(Ptr); } \ + void operator delete(void* Ptr, std::align_val_t Alignment) noexcept { NAMESPACE_REDCRAFT::Memory::Free(Ptr); } diff --git a/Redcraft.Utility/Source/Public/Memory/MemoryOperator.h b/Redcraft.Utility/Source/Public/Memory/MemoryOperator.h index aff921e..a2252a0 100644 --- a/Redcraft.Utility/Source/Public/Memory/MemoryOperator.h +++ b/Redcraft.Utility/Source/Public/Memory/MemoryOperator.h @@ -11,6 +11,12 @@ NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_BEGIN(Memory) +/** + * Default constructs a range of items in memory. + * + * @param Address - The address of the first memory location to construct at. + * @param Count - The number of elements to construct. + */ template FORCEINLINE void DefaultConstruct(void* Address, size_t Count = 1) { @@ -25,6 +31,13 @@ FORCEINLINE void DefaultConstruct(void* Address, size_t Count = 1) } } +/** + * Constructs a range of items into memory from a set of arguments. The arguments come from an another array. + * + * @param Destination - The memory location to start copying into. + * @param Source - A pointer to the first argument to pass to the constructor. + * @param Count - The number of elements to copy. + */ template requires (CConstructibleFrom) FORCEINLINE void Construct(void* Destination, const SourceElementType* Source, size_t Count = 1) @@ -45,6 +58,13 @@ FORCEINLINE void Construct(void* Destination, const SourceElementType* Source, s } } +/** + * Copy constructs a range of items into memory. + * + * @param Destination - The memory location to start copying into. + * @param Source - A pointer to the first item to copy from. + * @param Count - The number of elements to copy. + */ template FORCEINLINE void CopyConstruct(void* Destination, const ElementType* Source, size_t Count = 1) { @@ -64,6 +84,13 @@ FORCEINLINE void CopyConstruct(void* Destination, const ElementType* Source, siz } } +/** + * Move constructs a range of items into memory. + * + * @param Destination - The memory location to start moving into. + * @param Source - A pointer to the first item to move from. + * @param Count - The number of elements to move. + */ template FORCEINLINE void MoveConstruct(void* Destination, ElementType* Source, size_t Count = 1) { @@ -83,6 +110,13 @@ FORCEINLINE void MoveConstruct(void* Destination, ElementType* Source, size_t Co } } +/** + * Copy assigns a range of items. + * + * @param Destination - The memory location to start assigning to. + * @param Source - A pointer to the first item to assign. + * @param Count - The number of elements to assign. + */ template FORCEINLINE void CopyAssign(ElementType* Destination, const ElementType* Source, size_t Count = 1) { @@ -102,6 +136,13 @@ FORCEINLINE void CopyAssign(ElementType* Destination, const ElementType* Source, } } +/** + * Move assigns a range of items. + * + * @param Destination - The memory location to start assigning to. + * @param Source - A pointer to the first item to assign. + * @param Count - The number of elements to assign. + */ template FORCEINLINE void MoveAssign(ElementType* Destination, ElementType* Source, size_t Count = 1) { @@ -121,6 +162,12 @@ FORCEINLINE void MoveAssign(ElementType* Destination, ElementType* Source, size_ } } +/** + * Destructs a range of items in memory. + * + * @param Elements - A pointer to the first item to destruct. + * @param Count - The number of elements to destruct. + */ template FORCEINLINE void Destruct(ElementType* Element, size_t Count = 1) { diff --git a/Redcraft.Utility/Source/Public/Templates/Any.h b/Redcraft.Utility/Source/Public/Templates/Any.h index 69303ae..21c5870 100644 --- a/Redcraft.Utility/Source/Public/Templates/Any.h +++ b/Redcraft.Utility/Source/Public/Templates/Any.h @@ -27,14 +27,22 @@ concept CFAnyPlaceable = CDestructible> && CCopyConstructible, direct-initialized from Forward(InValue). */ + template requires (!CSameAs> && !CTInPlaceType> + && NAMESPACE_PRIVATE::CFAnyPlaceable && CConstructibleFrom, T&&>) + FORCEINLINE FAny(T&& InValue) : FAny(InPlaceType, Forward(InValue)) + { } + + /** Constructs an object with initial content an object of type TDecay, direct-non-list-initialized from Forward(Args).... */ template requires (NAMESPACE_PRIVATE::CFAnyPlaceable && CConstructibleFrom, Ts&&...>) FORCEINLINE explicit FAny(TInPlaceType, Ts&&... Args) { EmplaceImpl(Forward(Args)...); } - template requires (!CSameAs> && !CTInPlaceType> - && NAMESPACE_PRIVATE::CFAnyPlaceable && CConstructibleFrom, T&&>) - FORCEINLINE FAny(T&& InValue) : FAny(InPlaceType>, Forward(InValue)) - { } - + /** Destroys the contained object, if any, as if by a call to Reset(). */ FORCEINLINE ~FAny() { Destroy(); } + /** Assigns by copying the state of 'InValue'. This may use the object's copy constructor or copy assignment operator. */ FAny& operator=(const FAny& InValue) { - if (&InValue == this) return *this; + if (&InValue == this) UNLIKELY return *this; if (!InValue.IsValid()) { @@ -158,9 +171,10 @@ public: return *this; } + /** Assigns by moving the state of 'InValue'. This may use the object's move constructor or move assignment operator. */ FAny& operator=(FAny&& InValue) { - if (&InValue == this) return *this; + if (&InValue == this) UNLIKELY return *this; if (!InValue.IsValid()) { @@ -217,39 +231,54 @@ public: return *this; } + /** Assigns the type and value of 'InValue'. This may use the object's constructor or assignment operator. */ template requires (!CSameAs> && !CTInPlaceType> && NAMESPACE_PRIVATE::CFAnyPlaceable && CConstructibleFrom, T&&>) FORCEINLINE FAny& operator=(T&& InValue) { using DecayedType = TDecay; - if (HoldsAlternative()) + if constexpr (CAssignableFrom) { - GetValue() = Forward(InValue); - } - else - { - Destroy(); - EmplaceImpl(Forward(InValue)); + if (HoldsAlternative()) + { + GetValue() = Forward(InValue); + return *this; + } } + Destroy(); + EmplaceImpl(Forward(InValue)); + return *this; } - template requires (!CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr bool operator==(const T& InValue) const& + /** Check if the contained value is equivalent to 'InValue'. */ + template requires (!CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable && CEqualityComparable) + NODISCARD FORCEINLINE constexpr bool operator==(const T& InValue) const& { return HoldsAlternative() ? GetValue() == InValue : false; } - - template requires (!CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const& + + /** Check that the contained value is in ordered relationship with 'InValue'. */ + template requires (!CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable && CSynthThreeWayComparable) + NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const& { return HoldsAlternative() ? SynthThreeWayCompare(GetValue(), InValue) : partial_ordering::unordered; } - FORCEINLINE constexpr bool operator==(FInvalid) const& { return !IsValid(); } + /** @return true if instance does not contain a value, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool operator==(FInvalid) const& { return !IsValid(); } + /** + * Changes the contained object to one of type TDecay constructed from the arguments. + * First destroys the current contained object (if any) by Reset(), then constructs an object of type + * TDecay, direct-non-list-initialized from Forward(Args)..., as the contained object. + * + * @param Args - The arguments to be passed to the constructor of the contained object. + * + * @return A reference to the new contained object. + */ template requires (NAMESPACE_PRIVATE::CFAnyPlaceable && CConstructibleFrom, Ts&&...>) FORCEINLINE TDecay& Emplace(Ts&&... Args) { @@ -258,37 +287,48 @@ public: return GetValue>(); } - FORCEINLINE constexpr const type_info& GetTypeInfo() const { return IsValid() ? GetTypeInfoImpl() : typeid(void); } + /** @return The typeid of the contained value if instance is non-empty, otherwise typeid(void). */ + NODISCARD FORCEINLINE constexpr const type_info& GetTypeInfo() const { return IsValid() ? GetTypeInfoImpl() : typeid(void); } - FORCEINLINE constexpr bool IsValid() const { return TypeInfo != 0; } - FORCEINLINE constexpr explicit operator bool() const { return TypeInfo != 0; } + /** @return true if instance contains a value, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool IsValid() const { return TypeInfo != 0; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return TypeInfo != 0; } - template FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetTypeInfo() == typeid(T) : false; } + /** @return true if the any currently holds the alternative 'T', false otherwise. */ + template NODISCARD FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetTypeInfo() == typeid(T) : false; } + /** @return The contained object. */ template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr T& GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< T*>(GetStorage()); } - - template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr T&& GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< T*>(GetStorage())); } - - template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr const T& GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast(GetStorage()); } - - template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr const T&& GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(GetStorage())); } - - template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr T& Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } - - template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) - FORCEINLINE constexpr const T& Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } + NODISCARD FORCEINLINE constexpr T& GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< T*>(GetStorage()); } + /** @return The contained object. */ + template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) + NODISCARD FORCEINLINE constexpr T&& GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< T*>(GetStorage())); } + + /** @return The contained object. */ + template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) + NODISCARD FORCEINLINE constexpr const T& GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast(GetStorage()); } + + /** @return The contained object. */ + template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) + NODISCARD FORCEINLINE constexpr const T&& GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(GetStorage())); } + + /** @return The contained object when HoldsAlternative() returns true, 'DefaultValue' otherwise. */ + template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) + NODISCARD FORCEINLINE constexpr T& Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } + + /** @return The contained object when HoldsAlternative() returns true, 'DefaultValue' otherwise. */ + template requires (CSameAs> && NAMESPACE_PRIVATE::CFAnyPlaceable) + NODISCARD FORCEINLINE constexpr const T& Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } + + /** If not empty, destroys the contained object. */ FORCEINLINE void Reset() { Destroy(); Invalidate(); } + /** Overloads the Swap algorithm for FAny. */ friend void Swap(FAny& A, FAny& B) { if (!A.IsValid() && !B.IsValid()) return; diff --git a/Redcraft.Utility/Source/Public/Templates/Atomic.h b/Redcraft.Utility/Source/Public/Templates/Atomic.h index 5f53d3e..20e1d99 100644 --- a/Redcraft.Utility/Source/Public/Templates/Atomic.h +++ b/Redcraft.Utility/Source/Public/Templates/Atomic.h @@ -13,6 +13,16 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) +/** + * EMemoryOrder specifies how memory accesses, including regular, non-atomic memory accesses, + * are to be ordered around an atomic operation. Absent any constraints on a multi-core system, + * when multiple threads simultaneously read and write to several variables, one thread can observe + * the values change in an order different from the order another thread wrote them. Indeed, + * the apparent order of changes can even differ among multiple reader threads. Some similar effects + * can occur even on uniprocessor systems due to compiler transformations allowed by the memory model. + * + * @see https://en.cppreference.com/w/cpp/atomic/memory_order + */ enum class EMemoryOrder : uint8 { Relaxed = static_cast>(NAMESPACE_STD::memory_order_relaxed), @@ -64,63 +74,79 @@ public: using ValueType = T; + /** Indicates that the type is always lock-free */ static constexpr bool bIsAlwaysLockFree = NativeAtomicType::is_always_lock_free; + /** Indicates the required alignment of an object to be referenced by TAtomicRef. */ static constexpr size_t RequiredAlignment = NAMESPACE_STD::atomic_ref::required_alignment; + /** Constructs an atomic object. */ FORCEINLINE constexpr TAtomicImpl() requires (!bIsRef) : NativeAtomic() { }; FORCEINLINE constexpr TAtomicImpl(ValueType Desired) requires (!bIsRef) : NativeAtomic(Desired) { }; + /** Constructs an atomic reference. */ FORCEINLINE explicit TAtomicImpl(ValueType& Desired) requires (bIsRef) : NativeAtomic(Desired) { check(Memory::IsAligned(&Desired, RequiredAlignment)); }; FORCEINLINE TAtomicImpl(TAtomicImpl& InValue) requires (bIsRef) : NativeAtomic(InValue) { }; + /** Stores a value into an atomic object. */ FORCEINLINE ValueType operator=(ValueType Desired) { return NativeAtomic = Desired; } FORCEINLINE ValueType operator=(ValueType Desired) volatile requires (bIsAlwaysLockFree) { return NativeAtomic = Desired; } + /** Atomically replaces the value of the atomic object with a non-atomic argument. */ FORCEINLINE void Store(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.store(Desired, static_cast(Order)); } FORCEINLINE void Store(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.store(Desired, static_cast(Order)); } - FORCEINLINE ValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast(Order)); } - FORCEINLINE ValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile requires (bIsAlwaysLockFree) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast(Order)); } + /** Atomically obtains the value of the atomic object. */ + NODISCARD FORCEINLINE ValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast(Order)); } + NODISCARD FORCEINLINE ValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile requires (bIsAlwaysLockFree) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast(Order)); } - FORCEINLINE operator ValueType() const { return static_cast(NativeAtomic); } - FORCEINLINE operator ValueType() const volatile requires (bIsAlwaysLockFree) { return static_cast(NativeAtomic); } + /** Loads a value from an atomic object. */ + NODISCARD FORCEINLINE operator ValueType() const { return static_cast(NativeAtomic); } + NODISCARD FORCEINLINE operator ValueType() const volatile requires (bIsAlwaysLockFree) { return static_cast(NativeAtomic); } - FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { return NativeAtomic.exchange(Desired, static_cast(Order)); } - FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { return NativeAtomic.exchange(Desired, static_cast(Order)); } + /** Atomically replaces the value of the atomic object and obtains the value held previously. */ + NODISCARD FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { return NativeAtomic.exchange(Desired, static_cast(Order)); } + NODISCARD FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { return NativeAtomic.exchange(Desired, static_cast(Order)); } - FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false) - { - MEMORY_ORDER_CHECK(Failure, 0x01 | 0x02 | 0x04 | 0x20); - if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast(Success), static_cast(Failure)); - else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast(Success), static_cast(Failure)); - } - - FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree) + /** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */ + NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false) { MEMORY_ORDER_CHECK(Failure, 0x01 | 0x02 | 0x04 | 0x20); if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast(Success), static_cast(Failure)); else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast(Success), static_cast(Failure)); } - FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false) + /** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */ + NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree) + { + MEMORY_ORDER_CHECK(Failure, 0x01 | 0x02 | 0x04 | 0x20); + if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast(Success), static_cast(Failure)); + else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast(Success), static_cast(Failure)); + } + + /** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */ + NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false) { if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast(Order)); else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast(Order)); } - FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree) + /** Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. */ + NODISCARD FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false) volatile requires (bIsAlwaysLockFree) { if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast(Order)); else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast(Order)); } + /** Blocks the thread until notified and the atomic value changes. */ FORCEINLINE void Wait(ValueType Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); NativeAtomic.wait(Old, static_cast(Order)); } FORCEINLINE void Wait(ValueType Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); NativeAtomic.wait(Old, static_cast(Order)); } + /** Notifies at least one or all threads blocked waiting on the atomic object. */ FORCEINLINE void Notify(bool bIsAll = false) { if (bIsAll) NativeAtomic.notify_all(); else NativeAtomic.notify_one(); } FORCEINLINE void Notify(bool bIsAll = false) volatile { if (bIsAll) NativeAtomic.notify_all(); else NativeAtomic.notify_one(); } - + + /** Atomically executes the 'Func' on the value stored in the atomic object and obtains the value held previously. */ template requires (CInvocableResult) FORCEINLINE ValueType FetchFn(F&& Func, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { @@ -129,6 +155,7 @@ public: return Temp; } + /** Atomically executes the 'Func' on the value stored in the atomic object and obtains the value held previously. */ template requires (CInvocableResult && bIsAlwaysLockFree) FORCEINLINE ValueType FetchFn(F&& Func, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { @@ -137,87 +164,115 @@ public: return Temp; } + /** Atomically adds the argument to the value stored in the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchAdd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral || CFloatingPoint) { return NativeAtomic.fetch_add(InValue, static_cast(Order)); } FORCEINLINE ValueType FetchAdd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral || CFloatingPoint) && bIsAlwaysLockFree { return NativeAtomic.fetch_add(InValue, static_cast(Order)); } - + + /** Atomically adds the argument to the value stored in the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchAdd(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CPointer ) { return NativeAtomic.fetch_add(InValue, static_cast(Order)); } FORCEINLINE ValueType FetchAdd(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer && bIsAlwaysLockFree) { return NativeAtomic.fetch_add(InValue, static_cast(Order)); } - + + /** Atomically subtracts the argument from the value stored in the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchSub(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral || CFloatingPoint) { return NativeAtomic.fetch_sub(InValue, static_cast(Order)); } FORCEINLINE ValueType FetchSub(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral || CFloatingPoint) && bIsAlwaysLockFree { return NativeAtomic.fetch_sub(InValue, static_cast(Order)); } + /** Atomically subtracts the argument from the value stored in the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchSub(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CPointer ) { return NativeAtomic.fetch_sub(InValue, static_cast(Order)); } FORCEINLINE ValueType FetchSub(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer && bIsAlwaysLockFree) { return NativeAtomic.fetch_sub(InValue, static_cast(Order)); } - + + /** Atomically multiples the argument from the value stored in the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchMul(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral || CFloatingPoint) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old * InValue; }); } FORCEINLINE ValueType FetchMul(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral || CFloatingPoint) && bIsAlwaysLockFree { return FetchFn([InValue](ValueType Old) -> ValueType { return Old * InValue; }); } - + + /** Atomically divides the argument from the value stored in the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchDiv(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral || CFloatingPoint) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old / InValue; }); } FORCEINLINE ValueType FetchDiv(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral || CFloatingPoint) && bIsAlwaysLockFree { return FetchFn([InValue](ValueType Old) -> ValueType { return Old / InValue; }); } - + + /** Atomically models the argument from the value stored in the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchMod(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old % InValue; }); } FORCEINLINE ValueType FetchMod(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral && bIsAlwaysLockFree) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old % InValue; }); } + /** Atomically performs bitwise AND between the argument and the value of the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchAnd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral ) { return NativeAtomic.fetch_and(InValue, static_cast(Order)); } FORCEINLINE ValueType FetchAnd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral && bIsAlwaysLockFree) { return NativeAtomic.fetch_and(InValue, static_cast(Order)); } + /** Atomically performs bitwise OR between the argument and the value of the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchOr(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral ) { return NativeAtomic.fetch_or(InValue, static_cast(Order)); } FORCEINLINE ValueType FetchOr(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral && bIsAlwaysLockFree) { return NativeAtomic.fetch_or(InValue, static_cast(Order)); } + /** Atomically performs bitwise XOR between the argument and the value of the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchXor(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral ) { return NativeAtomic.fetch_xor(InValue, static_cast(Order)); } FORCEINLINE ValueType FetchXor(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral && bIsAlwaysLockFree) { return NativeAtomic.fetch_xor(InValue, static_cast(Order)); } + /** Atomically performs bitwise LSH between the argument and the value of the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchLsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old << InValue; }); } FORCEINLINE ValueType FetchLsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral && bIsAlwaysLockFree) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old << InValue; }); } + /** Atomically performs bitwise RSH between the argument and the value of the atomic object and obtains the value held previously. */ FORCEINLINE ValueType FetchRsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) requires (CIntegral ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old >> InValue; }); } FORCEINLINE ValueType FetchRsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral && bIsAlwaysLockFree) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old >> InValue; }); } + /** Increments the atomic value by one. */ FORCEINLINE ValueType operator++() requires ((CIntegral || CPointer) ) { return ++NativeAtomic; } FORCEINLINE ValueType operator++() volatile requires ((CIntegral || CPointer) && bIsAlwaysLockFree) { return ++NativeAtomic; } - + + /** Increments the atomic value by one. */ FORCEINLINE ValueType operator++(int) requires ((CIntegral || CPointer) ) { return NativeAtomic++; } FORCEINLINE ValueType operator++(int) volatile requires ((CIntegral || CPointer) && bIsAlwaysLockFree) { return NativeAtomic++; } - + + /** Decrements the atomic value by one. */ FORCEINLINE ValueType operator--() requires ((CIntegral || CPointer) ) { return --NativeAtomic; } FORCEINLINE ValueType operator--() volatile requires ((CIntegral || CPointer) && bIsAlwaysLockFree) { return --NativeAtomic; } - + + /** Decrements the atomic value by one. */ FORCEINLINE ValueType operator--(int) requires ((CIntegral || CPointer) ) { return NativeAtomic--; } FORCEINLINE ValueType operator--(int) volatile requires ((CIntegral || CPointer) && bIsAlwaysLockFree) { return NativeAtomic--; } + /** Adds with the atomic value. */ FORCEINLINE ValueType operator+=(ValueType InValue) requires ((CIntegral || CFloatingPoint) ) { return NativeAtomic += InValue; } FORCEINLINE ValueType operator+=(ValueType InValue) volatile requires ((CIntegral || CFloatingPoint) && bIsAlwaysLockFree) { return NativeAtomic += InValue; } - + + /** Adds with the atomic value. */ FORCEINLINE ValueType operator+=(ptrdiff InValue) requires (CPointer ) { return NativeAtomic += InValue; } FORCEINLINE ValueType operator+=(ptrdiff InValue) volatile requires (CPointer && bIsAlwaysLockFree) { return NativeAtomic += InValue; } - + + /** Subtracts with the atomic value. */ FORCEINLINE ValueType operator-=(ValueType InValue) requires ((CIntegral || CFloatingPoint) ) { return NativeAtomic -= InValue; } FORCEINLINE ValueType operator-=(ValueType InValue) volatile requires ((CIntegral || CFloatingPoint) && bIsAlwaysLockFree) { return NativeAtomic -= InValue; } - + + /** Subtracts with the atomic value. */ FORCEINLINE ValueType operator-=(ptrdiff InValue) requires (CPointer ) { return NativeAtomic -= InValue; } FORCEINLINE ValueType operator-=(ptrdiff InValue) volatile requires (CPointer && bIsAlwaysLockFree) { return NativeAtomic -= InValue; } - + + /** Multiples with the atomic value. */ FORCEINLINE ValueType operator*=(ValueType InValue) requires ((CIntegral || CFloatingPoint) ) { return FetchMul(InValue) * InValue; } FORCEINLINE ValueType operator*=(ValueType InValue) volatile requires ((CIntegral || CFloatingPoint) && bIsAlwaysLockFree) { return FetchMul(InValue) * InValue; } - + + /** Divides with the atomic value. */ FORCEINLINE ValueType operator/=(ValueType InValue) requires ((CIntegral || CFloatingPoint) ) { return FetchDiv(InValue) / InValue; } FORCEINLINE ValueType operator/=(ValueType InValue) volatile requires ((CIntegral || CFloatingPoint) && bIsAlwaysLockFree) { return FetchDiv(InValue) / InValue; } - + + /** Models with the atomic value. */ FORCEINLINE ValueType operator%=(ValueType InValue) requires (CIntegral ) { return FetchMod(InValue) % InValue; } FORCEINLINE ValueType operator%=(ValueType InValue) volatile requires (CIntegral && bIsAlwaysLockFree) { return FetchMod(InValue) % InValue; } - + + /** Performs bitwise AND with the atomic value. */ FORCEINLINE ValueType operator&=(ValueType InValue) requires (CIntegral ) { return NativeAtomic &= InValue; } FORCEINLINE ValueType operator&=(ValueType InValue) volatile requires (CIntegral && bIsAlwaysLockFree) { return NativeAtomic &= InValue; } - + + /** Performs bitwise OR with the atomic value. */ FORCEINLINE ValueType operator|=(ValueType InValue) requires (CIntegral ) { return NativeAtomic |= InValue; } FORCEINLINE ValueType operator|=(ValueType InValue) volatile requires (CIntegral && bIsAlwaysLockFree) { return NativeAtomic |= InValue; } - + + /** Performs bitwise XOR with the atomic value. */ FORCEINLINE ValueType operator^=(ValueType InValue) requires (CIntegral ) { return NativeAtomic ^= InValue; } FORCEINLINE ValueType operator^=(ValueType InValue) volatile requires (CIntegral && bIsAlwaysLockFree) { return NativeAtomic ^= InValue; } - + + /** Performs bitwise LSH with the atomic value. */ FORCEINLINE ValueType operator<<=(size_t InValue) requires (CIntegral ) { return FetchLsh(InValue) << InValue; } FORCEINLINE ValueType operator<<=(size_t InValue) volatile requires (CIntegral && bIsAlwaysLockFree) { return FetchLsh(InValue) << InValue; } - + + /** Performs bitwise RSH with the atomic value. */ FORCEINLINE ValueType operator>>=(size_t InValue) requires (CIntegral ) { return FetchRsh(InValue) >> InValue; } FORCEINLINE ValueType operator>>=(size_t InValue) volatile requires (CIntegral && bIsAlwaysLockFree) { return FetchRsh(InValue) >> InValue; } @@ -245,24 +300,34 @@ TAtomicRef(T&) -> TAtomicRef; static_assert(sizeof(TAtomic) == sizeof(int32), "The byte size of TAtomic is unexpected"); +/** + * FAtomicFlag is an atomic boolean type. Unlike all specializations of TAtomic, it is guaranteed to be lock-free. + * Unlike TAtomic, FAtomicFlag does not provide load or store operations. + */ struct FAtomicFlag : FSingleton { public: + /** Constructs an atomic flag. */ FORCEINLINE constexpr FAtomicFlag() : NativeAtomic() { }; + /** Atomically sets flag to false. */ FORCEINLINE void Clear(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.clear(static_cast(Order)); } FORCEINLINE void Clear(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.clear(static_cast(Order)); } + /** Atomically sets the flag to true and obtains its previous value. */ FORCEINLINE bool TestAndSet(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { return NativeAtomic.test_and_set(static_cast(Order)); } FORCEINLINE bool TestAndSet(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { return NativeAtomic.test_and_set(static_cast(Order)); } - FORCEINLINE bool Test(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.test(static_cast(Order)); } - FORCEINLINE bool Test(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.test(static_cast(Order)); } + /** Atomically returns the value of the flag. */ + NODISCARD FORCEINLINE bool Test(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.test(static_cast(Order)); } + NODISCARD FORCEINLINE bool Test(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.test(static_cast(Order)); } + /** Blocks the thread until notified and the atomic value changes. */ FORCEINLINE void Wait(bool Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); const_cast(NativeAtomic).wait(Old, static_cast(Order)); } FORCEINLINE void Wait(bool Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); const_cast(NativeAtomic).wait(Old, static_cast(Order)); } - + + /** Notifies at least one or all threads blocked waiting on the atomic object. */ FORCEINLINE void Notify(bool bIsAll = false) { if (bIsAll) const_cast(NativeAtomic).notify_all(); else const_cast(NativeAtomic).notify_one(); } FORCEINLINE void Notify(bool bIsAll = false) volatile { if (bIsAll) const_cast(NativeAtomic).notify_all(); else const_cast(NativeAtomic).notify_one(); } @@ -273,7 +338,7 @@ private: }; template -inline T KillDependency(T InValue) +NODISCARD inline T KillDependency(T InValue) { T Temp(InValue); return Temp; diff --git a/Redcraft.Utility/Source/Public/Templates/Function.h b/Redcraft.Utility/Source/Public/Templates/Function.h index 318d4fa..c4b1778 100644 --- a/Redcraft.Utility/Source/Public/Templates/Function.h +++ b/Redcraft.Utility/Source/Public/Templates/Function.h @@ -154,7 +154,7 @@ public: TFunctionStorage& operator=(const TFunctionStorage& InValue) requires (!bIsUnique) { - if (&InValue == this) return *this; + if (&InValue == this) UNLIKELY return *this; if (!InValue.IsValid()) { @@ -191,7 +191,7 @@ public: TFunctionStorage& operator=(TFunctionStorage&& InValue) { - if (&InValue == this) return *this; + if (&InValue == this) UNLIKELY return *this; if (!InValue.IsValid()) { @@ -469,6 +469,7 @@ public: FORCEINLINE constexpr TFunctionImpl& operator=(TFunctionImpl&&) = default; FORCEINLINE constexpr ~TFunctionImpl() = default; + /** Invokes the stored callable function target with the parameters args. */ FORCEINLINE ResultType operator()(Ts... Args) requires (CSameAs) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Ts... Args) & requires (CSameAs) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Ts... Args) && requires (CSameAs) { return CallImpl(Forward(Args)...); } @@ -476,10 +477,12 @@ public: FORCEINLINE ResultType operator()(Ts... Args) const& requires (CSameAs) { return CallImpl(Forward(Args)...); } FORCEINLINE ResultType operator()(Ts... Args) const&& requires (CSameAs) { return CallImpl(Forward(Args)...); } - FORCEINLINE constexpr bool operator==(nullptr_t) const& { return !IsValid(); } + /** @return false if instance stores a callable function target, true otherwise. */ + NODISCARD FORCEINLINE constexpr bool operator==(nullptr_t) const& { return !IsValid(); } - FORCEINLINE constexpr bool IsValid() const { return Storage.IsValid(); } - FORCEINLINE constexpr explicit operator bool() const { return Storage.IsValid(); } + /** @return true if instance stores a callable function target, false otherwise. */ + NODISCARD FORCEINLINE constexpr bool IsValid() const { return Storage.IsValid(); } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Storage.IsValid(); } private: @@ -494,7 +497,7 @@ private: return Callable(Storage.GetValuePtr(), Forward(Args)...); } -protected: // These functions should not be used by user-defined class +protected: // Use Invalidate() to invalidate the storage or use Emplace() to emplace a new object after destruction. FORCEINLINE constexpr void Destroy() { Storage.Destroy(); } @@ -534,6 +537,17 @@ protected: // These functions should not be used by user-defined class NAMESPACE_PRIVATE_END +/** + * A class which represents a reference to something callable. The important part here is *reference* - if + * you bind it to a lambda and the lambda goes out of scope, you will be left with an invalid reference. + * + * If you also want to take ownership of the callable thing, e.g. you want to return a lambda from a + * function, you should use TFunction. TFunctionRef does not concern itself with ownership because it's + * intended to be FAST. + * + * TFunctionRef is most useful when you want to parameterize a function with some caller-defined code + * without making it a template. + */ template class TFunctionRef : public NAMESPACE_PRIVATE::TFunctionImpl< @@ -550,17 +564,21 @@ private: public: + /** Remove the default initialization and disallow the construction of a TFunctionRef that does not store the function target. */ FORCEINLINE constexpr TFunctionRef() = delete; FORCEINLINE constexpr TFunctionRef(const TFunctionRef& InValue) = default; FORCEINLINE constexpr TFunctionRef(TFunctionRef&& InValue) = default; - // We delete the assignment operators because we don't want it to be confused with being related to - // regular C++ reference assignment - i.e. calling the assignment operator of whatever the reference - // is bound to - because that's not what TFunctionRef does, nor is it even capable of doing that. + /** + * We delete the assignment operators because we don't want it to be confused with being related to + * regular C++ reference assignment - i.e. calling the assignment operator of whatever the reference + * is bound to - because that's not what TFunctionRef does, nor is it even capable of doing that. + */ FORCEINLINE constexpr TFunctionRef& operator=(const TFunctionRef& InValue) = delete; FORCEINLINE constexpr TFunctionRef& operator=(TFunctionRef&& InValue) = delete; + /** Constructor which binds a TFunctionRef to a callable object. */ template requires (!CTFunctionRef> && NAMESPACE_PRIVATE::TIsInvocableSignature>::Value) FORCEINLINE constexpr TFunctionRef(T&& InValue) @@ -574,6 +592,12 @@ public: }; +/** + * A class which represents a copy of something callable. + * + * It takes a copy of whatever is bound to it, meaning you can return it from functions and store them in + * objects without caring about the lifetime of the original object being bound. + */ template class TFunction : public NAMESPACE_PRIVATE::TFunctionImpl< @@ -590,6 +614,7 @@ private: public: + /** Default constructor. */ FORCEINLINE constexpr TFunction(nullptr_t = nullptr) { Impl::Invalidate(); } FORCEINLINE TFunction(const TFunction& InValue) = default; @@ -597,6 +622,10 @@ public: FORCEINLINE TFunction& operator=(const TFunction& InValue) = default; FORCEINLINE TFunction& operator=(TFunction&& InValue) = default; + /** + * Constructs an TFunction with initial content an function object of type TDecay, + * direct-initialized from Forward(InValue). + */ template requires (!CTInPlaceType> && !CTFunctionRef> && !CTFunction> && !CTUniqueFunction> && CConstructibleFrom, T&&> && CCopyConstructible> @@ -607,7 +636,11 @@ public: if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::Invalidate(); else Impl::template Emplace(Forward(InValue)); } - + + /** + * Constructs an TFunction with initial content an function object of type TDecay, + * direct-non-list-initialized from Forward(Args).... + */ template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value && CConstructibleFrom, ArgTypes...> && CCopyConstructible> && CMoveConstructible> && CDestructible>) @@ -616,8 +649,10 @@ public: Impl::template Emplace(Forward(Args)...); } + /** Removes any bound callable from the TFunction, restoring it to the default empty state. */ FORCEINLINE constexpr TFunction& operator=(nullptr_t) { Reset(); return *this; } + /** Assigns the type and value of 'InValue'. */ template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value && !CTFunctionRef> && !CTFunction> && !CTUniqueFunction> && CConstructibleFrom, T&&> && CCopyConstructible> @@ -630,6 +665,15 @@ public: return *this; } + /** + * Changes the function object to one of type TDecay constructed from the arguments. + * First destroys the current function object (if any) by Reset(), then constructs an object of type + * TDecay, direct-non-list-initialized from Forward(Args)..., as the function object. + * + * @param Args - The arguments to be passed to the constructor of the function object. + * + * @return A reference to the new function object. + */ template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value && CConstructibleFrom, ArgTypes...> && CCopyConstructible> && CMoveConstructible> && CDestructible>) @@ -639,12 +683,20 @@ public: return Impl::template Emplace(Forward(Args)...); } + /** Removes any bound callable from the TFunction, restoring it to the default empty state. */ FORCEINLINE constexpr void Reset() { Impl::Destroy(); Impl::Invalidate(); } + /** Overloads the Swap algorithm for TFunction. */ friend FORCEINLINE constexpr void Swap(TFunction& A, TFunction& B) { Swap(static_cast(A), static_cast(B)); } }; +/** + * A class which represents a copy of something callable, but is move-only. + * + * It takes a copy of whatever is bound to it, meaning you can return it from functions and store them in + * objects without caring about the lifetime of the original object being bound. + */ template class TUniqueFunction : public NAMESPACE_PRIVATE::TFunctionImpl< @@ -661,6 +713,7 @@ private: public: + /** Default constructor. */ FORCEINLINE constexpr TUniqueFunction(nullptr_t = nullptr) { Impl::Invalidate(); } FORCEINLINE TUniqueFunction(const TUniqueFunction& InValue) = delete; @@ -668,28 +721,36 @@ public: FORCEINLINE TUniqueFunction& operator=(const TUniqueFunction& InValue) = delete; FORCEINLINE TUniqueFunction& operator=(TUniqueFunction&& InValue) = default; + /** Constructor from TFunction to TUniqueFunction. */ FORCEINLINE TUniqueFunction(const TFunction& InValue) { new (this) TFunction(InValue); } + /** Constructor from TFunction to TUniqueFunction. */ FORCEINLINE TUniqueFunction(TFunction&& InValue) { new (this) TFunction(MoveTemp(InValue)); } + /** Assignment operator from TFunction to TUniqueFunction. */ FORCEINLINE TUniqueFunction& operator=(const TFunction& InValue) { *reinterpret_cast*>(this) = InValue; return *this; } + /** Assignment operator from TFunction to TUniqueFunction. */ FORCEINLINE TUniqueFunction& operator=(TFunction&& InValue) { *reinterpret_cast*>(this) = MoveTemp(InValue); return *this; } + /** + * Constructs an TUniqueFunction with initial content an function object of type TDecay, + * direct-initialized from Forward(InValue). + */ template requires (!CTInPlaceType> && !CTFunctionRef> && !CTFunction> && !CTUniqueFunction> && CConstructibleFrom, T&&> && CMoveConstructible> && CDestructible> @@ -699,7 +760,11 @@ public: if (!NAMESPACE_PRIVATE::FunctionIsBound(InValue)) Impl::Invalidate(); else Impl::template Emplace(Forward(InValue)); } - + + /** + * Constructs an TUniqueFunction with initial content an function object of type TDecay, + * direct-non-list-initialized from Forward(Args).... + */ template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value && CConstructibleFrom, ArgTypes...> && CMoveConstructible> && CDestructible>) FORCEINLINE explicit TUniqueFunction(TInPlaceType, ArgTypes&&... Args) @@ -707,6 +772,7 @@ public: Impl::template Emplace(Forward(Args)...); } + /** Removes any bound callable from the TUniqueFunction, restoring it to the default empty state. */ FORCEINLINE constexpr TUniqueFunction& operator=(nullptr_t) { Impl::Destroy(); Impl::Invalidate(); return *this; } template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value @@ -720,6 +786,15 @@ public: return *this; } + /** + * Changes the function object to one of type TDecay constructed from the arguments. + * First destroys the current function object (if any) by Reset(), then constructs an object of type + * TDecay, direct-non-list-initialized from Forward(Args)..., as the function object. + * + * @param Args - The arguments to be passed to the constructor of the function object. + * + * @return A reference to the new function object. + */ template requires (NAMESPACE_PRIVATE::TIsInvocableSignature>::Value && CConstructibleFrom, ArgTypes...> && CMoveConstructible> && CDestructible>) FORCEINLINE TDecay& Emplace(ArgTypes&&... Args) @@ -729,8 +804,10 @@ public: return Impl::template Emplace(Forward(Args)...); } + /** Removes any bound callable from the TUniqueFunction, restoring it to the default empty state. */ FORCEINLINE constexpr void Reset() { Impl::Destroy(); Impl::Invalidate(); } + /** Overloads the Swap algorithm for TUniqueFunction. */ friend FORCEINLINE constexpr void Swap(TUniqueFunction& A, TUniqueFunction& B) { Swap(static_cast(A), static_cast(B)); } }; @@ -779,8 +856,9 @@ struct TNotFunction NAMESPACE_PRIVATE_END -template requires (CConstructibleFrom) -FORCEINLINE constexpr NAMESPACE_PRIVATE::TNotFunction> NotFn(F&& Func) +/** Creates a forwarding call wrapper that returns the negation of the callable object it holds. */ +template requires (CConstructibleFrom && CMoveConstructible) +NODISCARD FORCEINLINE constexpr NAMESPACE_PRIVATE::TNotFunction> NotFn(F&& Func) { return { Forward(Func) }; } diff --git a/Redcraft.Utility/Source/Public/Templates/Invoke.h b/Redcraft.Utility/Source/Public/Templates/Invoke.h index d46f83b..9829e64 100644 --- a/Redcraft.Utility/Source/Public/Templates/Invoke.h +++ b/Redcraft.Utility/Source/Public/Templates/Invoke.h @@ -81,6 +81,7 @@ struct InvokeImpl : InvokeMember { }; NAMESPACE_PRIVATE_END +/** Invoke the Callable object f with the parameters args. */ template requires (CInvocable) FORCEINLINE constexpr auto Invoke(F&& Func, Ts&&... Args) -> decltype(NAMESPACE_PRIVATE::InvokeImpl::Invoke(Forward(Func), Forward(Args)...)) @@ -88,8 +89,9 @@ FORCEINLINE constexpr auto Invoke(F&& Func, Ts&&... Args) return NAMESPACE_PRIVATE::InvokeImpl::Invoke(Forward(Func), Forward(Args)...); } +/** Invoke the Callable object f with the parameters args. */ template requires (CInvocableResult) -FORCEINLINE constexpr R InvokeResult(F&& Func, Ts&&... Args) +NODISCARD FORCEINLINE constexpr R InvokeResult(F&& Func, Ts&&... Args) { if constexpr (CVoid) Invoke(Forward(Func), Forward(Args)...); else return Invoke(Forward(Func), Forward(Args)...); diff --git a/Redcraft.Utility/Source/Public/Templates/Noncopyable.h b/Redcraft.Utility/Source/Public/Templates/Noncopyable.h index 7b5b45a..ab98b55 100644 --- a/Redcraft.Utility/Source/Public/Templates/Noncopyable.h +++ b/Redcraft.Utility/Source/Public/Templates/Noncopyable.h @@ -6,6 +6,7 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) +/** A class indicates that a derived class cannot be copied. */ struct FNoncopyable { FNoncopyable() = default; @@ -13,6 +14,7 @@ struct FNoncopyable FNoncopyable& operator=(const FNoncopyable&) = delete; }; +/** A class indicates that a derived class cannot be moved. */ struct FNonmovable { FNonmovable() = default; @@ -20,9 +22,11 @@ struct FNonmovable FNonmovable& operator=(FNonmovable&&) = delete; }; -// Multiple inheritance is no longer used here, as that would break the EBO in MSVC +/** A class indicates that a derived class cannot be copied or moved. */ struct FSingleton // : FNoncopyable, FNonmovable { + // NOTE: Multiple inheritance is no longer used here, as that would break the EBO in MSVC + FSingleton() = default; FSingleton(const FSingleton&) = delete; FSingleton(FSingleton&&) = delete; diff --git a/Redcraft.Utility/Source/Public/Templates/Optional.h b/Redcraft.Utility/Source/Public/Templates/Optional.h index 3217030..c8cfe44 100644 --- a/Redcraft.Utility/Source/Public/Templates/Optional.h +++ b/Redcraft.Utility/Source/Public/Templates/Optional.h @@ -38,6 +38,7 @@ NAMESPACE_PRIVATE_END template concept CTOptional = NAMESPACE_PRIVATE::TIsTOptional>::Value; +/** The class template manages an optional contained value, i.e. a value that may or may not be present. */ template requires (CDestructible) class TOptional { @@ -45,10 +46,20 @@ public: using ValueType = OptionalType; + /** Constructs an object that does not contain a value. */ FORCEINLINE constexpr TOptional() : bIsValid(false) { } + /** Constructs an object that does not contain a value. */ FORCEINLINE constexpr TOptional(FInvalid) : TOptional() { } + /** Constructs an object with initial content an object, direct-initialized from Forward(InValue). */ + template requires (CConstructibleFrom) + && (!CSameAs, FInPlace>) && (!CSameAs>) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(T&& InValue) + : TOptional(InPlace, Forward(InValue)) + { } + + /** Constructs an object with initial content an object, direct-non-list-initialized from Forward(Args).... */ template requires (CConstructibleFrom) FORCEINLINE constexpr explicit TOptional(FInPlace, Ts&&... Args) : bIsValid(true) @@ -56,28 +67,27 @@ public: new (&Value) OptionalType(Forward(Args)...); } - template requires (CConstructibleFrom) - && (!CSameAs, FInPlace>) && (!CSameAs>) - FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(T&& InValue) - : TOptional(InPlace, Forward(InValue)) - { } - + /** Copies content of other into a new instance. */ FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CTriviallyCopyConstructible) = default; + /** Copies content of other into a new instance. */ FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CCopyConstructible && !CTriviallyCopyConstructible) : bIsValid(InValue.IsValid()) { if (InValue.IsValid()) new (&Value) OptionalType(InValue.GetValue()); } + /** Moves content of other into a new instance. */ FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CTriviallyMoveConstructible) = default; + /** Moves content of other into a new instance. */ FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CMoveConstructible && !CTriviallyMoveConstructible) : bIsValid(InValue.IsValid()) { if (InValue.IsValid()) new (&Value) OptionalType(MoveTemp(InValue.GetValue())); } + /** Converting copy constructor. */ template requires (CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(const TOptional& InValue) : bIsValid(InValue.IsValid()) @@ -85,6 +95,7 @@ public: if (InValue.IsValid()) new (&Value) OptionalType(InValue.GetValue()); } + /** Converting move constructor. */ template requires (CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(TOptional&& InValue) : bIsValid(InValue.IsValid()) @@ -92,15 +103,19 @@ public: if (InValue.IsValid()) new (&Value) OptionalType(MoveTemp(InValue.GetValue())); } + /** Destroys the contained object, if any, as if by a call to Reset(). */ FORCEINLINE constexpr ~TOptional() requires (CTriviallyDestructible) = default; + /** Destroys the contained object, if any, as if by a call to Reset(). */ FORCEINLINE constexpr ~TOptional() requires (!CTriviallyDestructible) { Reset(); } + /** Assigns by copying the state of 'InValue'. */ FORCEINLINE constexpr TOptional& operator=(const TOptional& InValue) requires (CTriviallyCopyConstructible && CTriviallyCopyAssignable) = default; + /** Assigns by copying the state of 'InValue'. */ constexpr TOptional& operator=(const TOptional& InValue) requires (CCopyConstructible && CCopyAssignable && !CTriviallyCopyConstructible && !CTriviallyCopyAssignable) { @@ -122,8 +137,10 @@ public: return *this; } + /** Assigns by moving the state of 'InValue'. */ FORCEINLINE constexpr TOptional& operator=(TOptional&& InValue) requires (CTriviallyMoveConstructible && CTriviallyMoveAssignable) = default; + /** Assigns by moving the state of 'InValue'. */ constexpr TOptional& operator=(TOptional&& InValue) requires (CMoveConstructible && CMoveAssignable && !CTriviallyMoveConstructible && !CTriviallyMoveAssignable) { @@ -145,6 +162,7 @@ public: return *this; } + /** Assigns by copying the state of 'InValue'. */ template requires (CConstructibleFrom && CAssignableFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) constexpr TOptional& operator=(const TOptional& InValue) @@ -165,6 +183,7 @@ public: return *this; } + /** Assigns by moving the state of 'InValue'. */ template requires (CConstructibleFrom && CAssignableFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) constexpr TOptional& operator=(TOptional&& InValue) @@ -185,6 +204,7 @@ public: return *this; } + /** Assigns the value of 'InValue'. */ template requires (CConstructibleFrom && CAssignableFrom) FORCEINLINE constexpr TOptional& operator=(T&& InValue) { @@ -198,36 +218,50 @@ public: return *this; } + /** Check if the two optional are equivalent. */ template requires (CWeaklyEqualityComparable) - friend FORCEINLINE constexpr bool operator==(const TOptional& LHS, const TOptional& RHS) + NODISCARD friend FORCEINLINE constexpr bool operator==(const TOptional& LHS, const TOptional& RHS) { if (LHS.IsValid() != RHS.IsValid()) return false; if (LHS.IsValid() == false) return true; return *LHS == *RHS; } + /** Check the order relationship between two optional. */ template requires (CSynthThreeWayComparable) - friend FORCEINLINE constexpr partial_ordering operator<=>(const TOptional& LHS, const TOptional& RHS) + NODISCARD friend FORCEINLINE constexpr partial_ordering operator<=>(const TOptional& LHS, const TOptional& RHS) { if (LHS.IsValid() != RHS.IsValid()) return partial_ordering::unordered; if (LHS.IsValid() == false) return partial_ordering::equivalent; return SynthThreeWayCompare(*LHS, *RHS); } + /** Check if the optional value is equivalent to 'InValue'. */ template requires (!CTOptional&& CWeaklyEqualityComparable) - FORCEINLINE constexpr bool operator==(const T& InValue) const& + NODISCARD FORCEINLINE constexpr bool operator==(const T& InValue) const& { return IsValid() ? GetValue() == InValue : false; } - + + /** Check that the optional value is in ordered relationship with 'InValue'. */ template requires (!CTOptional&& CSynthThreeWayComparable) - FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const& + NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const& { return IsValid() ? SynthThreeWayCompare(GetValue(), InValue) : partial_ordering::unordered; } - FORCEINLINE constexpr bool operator==(FInvalid) const& { return !IsValid(); } + /** @return true if instance does not contain a value, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool operator==(FInvalid) const& { return !IsValid(); } + /** + * Changes the contained object to one constructed from the arguments. + * First destroys the current contained object (if any) by Reset(), + * then constructs an object, direct-non-list-initialized from Forward(Args)..., as the contained object. + * + * @param Args - The arguments to be passed to the constructor of the object. + * + * @return A reference to the new object. + */ template requires (CConstructibleFrom) FORCEINLINE constexpr OptionalType& Emplace(ArgTypes&&... Args) { @@ -239,25 +273,31 @@ public: return *Result; } - FORCEINLINE constexpr bool IsValid() const { return bIsValid; } - FORCEINLINE constexpr explicit operator bool() const { return bIsValid; } - - FORCEINLINE constexpr OptionalType& GetValue() & { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast< OptionalType*>(&Value); } - FORCEINLINE constexpr OptionalType&& GetValue() && { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< OptionalType*>(&Value)); } - FORCEINLINE constexpr const OptionalType& GetValue() const& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast(&Value); } - FORCEINLINE constexpr const OptionalType&& GetValue() const&& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(&Value)); } + /** @return true if instance contains a value, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool IsValid() const { return bIsValid; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return bIsValid; } - FORCEINLINE constexpr const OptionalType* operator->() const { return &GetValue(); } - FORCEINLINE constexpr OptionalType* operator->() { return &GetValue(); } + /** @return The contained object. */ + NODISCARD FORCEINLINE constexpr OptionalType& GetValue() & { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast< OptionalType*>(&Value); } + NODISCARD FORCEINLINE constexpr OptionalType&& GetValue() && { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< OptionalType*>(&Value)); } + NODISCARD FORCEINLINE constexpr const OptionalType& GetValue() const& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast(&Value); } + NODISCARD FORCEINLINE constexpr const OptionalType&& GetValue() const&& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(&Value)); } - FORCEINLINE constexpr OptionalType& operator*() & { return GetValue(); } - FORCEINLINE constexpr OptionalType&& operator*() && { return GetValue(); } - FORCEINLINE constexpr const OptionalType& operator*() const& { return GetValue(); } - FORCEINLINE constexpr const OptionalType&& operator*() const&& { return GetValue(); } + /** @return The pointer to the contained object. */ + NODISCARD FORCEINLINE constexpr const OptionalType* operator->() const { return &GetValue(); } + NODISCARD FORCEINLINE constexpr OptionalType* operator->() { return &GetValue(); } - FORCEINLINE constexpr OptionalType& Get( OptionalType& DefaultValue) & { return IsValid() ? GetValue() : DefaultValue; } - FORCEINLINE constexpr const OptionalType& Get(const OptionalType& DefaultValue) const& { return IsValid() ? GetValue() : DefaultValue; } + /** @return The contained object. */ + NODISCARD FORCEINLINE constexpr OptionalType& operator*() & { return GetValue(); } + NODISCARD FORCEINLINE constexpr OptionalType&& operator*() && { return GetValue(); } + NODISCARD FORCEINLINE constexpr const OptionalType& operator*() const& { return GetValue(); } + NODISCARD FORCEINLINE constexpr const OptionalType&& operator*() const&& { return GetValue(); } + /** @return The contained object when IsValid() returns true, 'DefaultValue' otherwise. */ + NODISCARD FORCEINLINE constexpr OptionalType& Get( OptionalType& DefaultValue) & { return IsValid() ? GetValue() : DefaultValue; } + NODISCARD FORCEINLINE constexpr const OptionalType& Get(const OptionalType& DefaultValue) const& { return IsValid() ? GetValue() : DefaultValue; } + + /** If not empty, destroys the contained object. */ FORCEINLINE constexpr void Reset() { if (bIsValid) @@ -269,14 +309,15 @@ public: } } - friend FORCEINLINE constexpr size_t GetTypeHash(const TOptional& A) requires (CHashable) + /** Overloads the GetTypeHash algorithm for TOptional. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TOptional& A) requires (CHashable) { if (!A.IsValid()) return 2824517378; return GetTypeHash(A.GetValue()); } - template requires (CMoveConstructible && CSwappable) - friend constexpr void Swap(TOptional& A, TOptional& B) + /** Overloads the Swap algorithm for TOptional. */ + friend constexpr void Swap(TOptional& A, TOptional& B) requires (CMoveConstructible && CSwappable) { if (!A.IsValid() && !B.IsValid()) return; @@ -306,20 +347,23 @@ private: template TOptional(T) -> TOptional; +/** Creates an optional object that does not contain a value. */ template requires (CDestructible) -FORCEINLINE constexpr TOptional> MakeOptional(FInvalid) +NODISCARD FORCEINLINE constexpr TOptional> MakeOptional(FInvalid) { return TOptional>(Invalid); } +/** Creates an optional object from value. */ template requires (CDestructible && CConstructibleFrom) -FORCEINLINE constexpr TOptional MakeOptional(T&& InValue) +NODISCARD FORCEINLINE constexpr TOptional MakeOptional(T&& InValue) { return TOptional(Forward(InValue)); } +/** Creates an optional object constructed in-place from args.... */ template requires (CDestructible && CConstructibleFrom) -FORCEINLINE constexpr TOptional MakeOptional(Ts&&... Args) +NODISCARD FORCEINLINE constexpr TOptional MakeOptional(Ts&&... Args) { return TOptional(InPlace, Forward(Args)...); } diff --git a/Redcraft.Utility/Source/Public/Templates/ReferenceWrapper.h b/Redcraft.Utility/Source/Public/Templates/ReferenceWrapper.h index 59bf693..1c9e0e0 100644 --- a/Redcraft.Utility/Source/Public/Templates/ReferenceWrapper.h +++ b/Redcraft.Utility/Source/Public/Templates/ReferenceWrapper.h @@ -10,6 +10,11 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) +/** + * TReferenceWrapper is a class template that wraps a reference object. + * It is frequently used as a mechanism to store references inside standard + * containers which cannot normally hold references. + */ template requires (CObject || CFunction) class TReferenceWrapper { @@ -17,6 +22,7 @@ public: using Type = ReferencedType; + /** Constructs a new reference wrapper. */ template requires (CConvertibleTo) FORCEINLINE constexpr TReferenceWrapper(T&& Object) { @@ -24,34 +30,47 @@ public: Pointer = AddressOf(Reference); } + /** Copies/moves content of other into a new instance. */ FORCEINLINE constexpr TReferenceWrapper(const TReferenceWrapper&) = default; FORCEINLINE constexpr TReferenceWrapper(TReferenceWrapper&&) = default; - + + /** Converting copy constructor. */ template requires (CConvertibleTo) FORCEINLINE constexpr TReferenceWrapper(const TReferenceWrapper& InValue) : Pointer(InValue.Pointer) { } + /** Assign a value to the referenced object. */ template requires (CAssignableFrom) FORCEINLINE constexpr TReferenceWrapper& operator=(T&& Object) { Get() = Forward(Object); return *this; } + /** Remove the assignment operator, as rebinding is not allowed. */ FORCEINLINE constexpr TReferenceWrapper& operator=(const TReferenceWrapper&) = delete; FORCEINLINE constexpr TReferenceWrapper& operator=(TReferenceWrapper&&) = delete; - FORCEINLINE constexpr operator ReferencedType&() const { return *Pointer; } + /** @return The stored reference. */ FORCEINLINE constexpr ReferencedType& Get() const { return *Pointer; } + FORCEINLINE constexpr operator ReferencedType&() const { return *Pointer; } + /** Calls the Callable object, reference to which is stored. */ template FORCEINLINE constexpr TInvokeResult operator()(Ts&&... Args) const { return Invoke(Get(), Forward(Args)...); } - friend FORCEINLINE constexpr size_t GetTypeHash(TReferenceWrapper A) requires (CHashable) + /** Overloads the GetTypeHash algorithm for TReferenceWrapper. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(TReferenceWrapper A) requires (CHashable) { return GetTypeHash(A.Get()); } - + + /** Overloads the Swap algorithm for TReferenceWrapper. */ + friend FORCEINLINE constexpr void Swap(TReferenceWrapper A, TReferenceWrapper B) requires (CSwappable) + { + Swap(A.Get(), B.Get()); + } + private: ReferencedType* Pointer; diff --git a/Redcraft.Utility/Source/Public/Templates/Tuple.h b/Redcraft.Utility/Source/Public/Templates/Tuple.h index 8f97565..adac48c 100644 --- a/Redcraft.Utility/Source/Public/Templates/Tuple.h +++ b/Redcraft.Utility/Source/Public/Templates/Tuple.h @@ -176,9 +176,9 @@ protected: FORCEINLINE constexpr TTupleImpl& operator=(TTupleImpl&&) = default; FORCEINLINE constexpr ~TTupleImpl() = default; - template - FORCEINLINE constexpr explicit TTupleImpl(FForwardingConstructor, ArgTypes&&... Args) - : TTupleBasicElement(Forward(Args))... + template + FORCEINLINE constexpr explicit TTupleImpl(FForwardingConstructor, Us&&... Args) + : TTupleBasicElement(Forward(Args))... { } template @@ -301,14 +301,17 @@ private: public: + /** Default constructor. Value-initializes all elements, if any. */ FORCEINLINE constexpr TTuple() = default; - template requires (sizeof...(Ts) >= 1 && sizeof...(ArgTypes) == sizeof...(Ts)) - && (true && ... && CConstructibleFrom) - FORCEINLINE constexpr explicit (!(true && ... && CConvertibleTo)) TTuple(ArgTypes&&... Args) - : Super(NAMESPACE_PRIVATE::ForwardingConstructor, Forward(Args)...) + /** Converting constructor. Initializes each element of the tuple with the corresponding value in Forward(Args). */ + template requires (sizeof...(Ts) >= 1 && sizeof...(Us) == sizeof...(Ts)) + && (true && ... && CConstructibleFrom) + FORCEINLINE constexpr explicit (!(true && ... && CConvertibleTo)) TTuple(Us&&... Args) + : Super(NAMESPACE_PRIVATE::ForwardingConstructor, Forward(Args)...) { } + /** Converting copy constructor. Initializes each element of the tuple with the corresponding element of other. */ template requires (sizeof...(Us) == sizeof...(Ts) && (true && ... && CConstructibleFrom) && NAMESPACE_PRIVATE::TTupleConvertCopy::Value) @@ -316,6 +319,7 @@ public: : Super(NAMESPACE_PRIVATE::OtherTupleConstructor, InValue) { } + /** Converting move constructor. Initializes each element of the tuple with the corresponding element of other. */ template requires (sizeof...(Us) == sizeof...(Ts) && (true && ... && CConstructibleFrom) && NAMESPACE_PRIVATE::TTupleConvertMove::Value) @@ -323,9 +327,11 @@ public: : Super(NAMESPACE_PRIVATE::OtherTupleConstructor, MoveTemp(InValue)) { } + /** Copies/moves content of other into a new instance. */ FORCEINLINE constexpr TTuple(const TTuple&) = default; FORCEINLINE constexpr TTuple(TTuple&&) = default; + /** Converting copy assignment operator. Assigns each element of other to the corresponding element of this. */ template requires (sizeof...(Us) == sizeof...(Ts) && (true && ... && CAssignableFrom)) FORCEINLINE constexpr TTuple& operator=(const TTuple& InValue) @@ -334,6 +340,7 @@ public: return *this; } + /** Converting move assignment operator. Assigns each element of other to the corresponding element of this. */ template requires (sizeof...(Us) == sizeof...(Ts) && (true && ... && CAssignableFrom)) FORCEINLINE constexpr TTuple& operator=(TTuple&& InValue) @@ -342,11 +349,13 @@ public: return *this; } + /** Copy/move assignment operator. */ FORCEINLINE constexpr TTuple& operator=(const TTuple&) = default; FORCEINLINE constexpr TTuple& operator=(TTuple&&) = default; + /** Compares every element of the tuple lhs with the corresponding element of the tuple rhs. */ template requires (sizeof...(Ts) == sizeof...(Us) && NAMESPACE_PRIVATE::CTTupleWeaklyEqualityComparable, TTypeSequence>) - friend FORCEINLINE constexpr bool operator==(const TTuple& LHS, const TTuple& RHS) + NODISCARD friend FORCEINLINE constexpr bool operator==(const TTuple& LHS, const TTuple& RHS) { if constexpr (sizeof...(Ts) != sizeof...(Us)) return false; @@ -357,31 +366,35 @@ public: (TMakeIndexSequence()); } + /** Compares lhs and rhs lexicographically by synthesized three-way comparison. */ template requires (sizeof...(Ts) == sizeof...(Us) && NAMESPACE_PRIVATE::CTTupleSynthThreeWayComparable, TTypeSequence>) - friend FORCEINLINE constexpr TCommonComparisonCategory...> operator<=>(const TTuple& LHS, const TTuple& RHS) + NODISCARD friend FORCEINLINE constexpr TCommonComparisonCategory...> operator<=>(const TTuple& LHS, const TTuple& RHS) { using R = TCommonComparisonCategory...>; return NAMESPACE_PRIVATE::TTupleThreeWay>::Do(LHS, RHS); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement>, I>& >(*this).GetValue(); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast>, I>& >(*this).GetValue(); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement>, I>& >(*this).GetValue(); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast>, I>& >(*this).GetValue(); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement>, I>&&>(*this).GetValue(); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast>, I>&&>(*this).GetValue(); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement>, I>&&>(*this).GetValue(); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast>, I>&&>(*this).GetValue(); } + /** Extracts the Ith element from the tuple. I must be an integer value in [0, sizeof...(Ts)). */ + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement>, I>& >(*this).GetValue(); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast>, I>& >(*this).GetValue(); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement>, I>& >(*this).GetValue(); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast>, I>& >(*this).GetValue(); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement>, I>&&>(*this).GetValue(); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast>, I>&&>(*this).GetValue(); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement>, I>&&>(*this).GetValue(); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast>, I>&&>(*this).GetValue(); } - template FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< TTuple& >(*this).GetValue>>(); } - template FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast(*this).GetValue>>(); } - template FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile TTuple& >(*this).GetValue>>(); } - template FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast(*this).GetValue>>(); } - template FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< TTuple&&>(*this).GetValue>>(); } - template FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast(*this).GetValue>>(); } - template FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile TTuple&&>(*this).GetValue>>(); } - template FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast(*this).GetValue>>(); } + /** Extracts the element of the tuple whose type is T. Fails to compile unless the tuple has exactly one element of that type. */ + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< TTuple& >(*this).GetValue>>(); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast(*this).GetValue>>(); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile TTuple& >(*this).GetValue>>(); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast(*this).GetValue>>(); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< TTuple&&>(*this).GetValue>>(); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast(*this).GetValue>>(); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile TTuple&&>(*this).GetValue>>(); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast(*this).GetValue>>(); } + /** Invoke the Callable object 'Func' with a tuple of arguments. */ template requires (CInvocable) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) & { return Helper::Apply(Forward(Func), static_cast< TTuple& >(*this)); } template requires (CInvocable) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const & { return Helper::Apply(Forward(Func), static_cast(*this)); } template requires (CInvocable) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile& { return Helper::Apply(Forward(Func), static_cast< volatile TTuple& >(*this)); } @@ -391,25 +404,28 @@ public: template requires (CInvocable) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile&& { return Helper::Apply(Forward(Func), static_cast< volatile TTuple&&>(*this)); } template requires (CInvocable) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile&& { return Helper::Apply(Forward(Func), static_cast(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) & { return Helper::Transform(Forward(Func), static_cast< TTuple& >(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const & { return Helper::Transform(Forward(Func), static_cast(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile& { return Helper::Transform(Forward(Func), static_cast< volatile TTuple& >(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile& { return Helper::Transform(Forward(Func), static_cast(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) && { return Helper::Transform(Forward(Func), static_cast< TTuple&&>(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const && { return Helper::Transform(Forward(Func), static_cast(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile&& { return Helper::Transform(Forward(Func), static_cast< volatile TTuple&&>(*this)); } - template requires (true && ... && (CInvocable && !CSameAs>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile&& { return Helper::Transform(Forward(Func), static_cast(*this)); } + /** Transform a tuple into another tuple using the given function. */ + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) & { return Helper::Transform(Forward(Func), static_cast< TTuple& >(*this)); } + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const & { return Helper::Transform(Forward(Func), static_cast(*this)); } + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile& { return Helper::Transform(Forward(Func), static_cast< volatile TTuple& >(*this)); } + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile& { return Helper::Transform(Forward(Func), static_cast(*this)); } + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) && { return Helper::Transform(Forward(Func), static_cast< TTuple&&>(*this)); } + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const && { return Helper::Transform(Forward(Func), static_cast(*this)); } + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile&& { return Helper::Transform(Forward(Func), static_cast< volatile TTuple&&>(*this)); } + template requires (true && ... && (CInvocable && !CSameAs>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile&& { return Helper::Transform(Forward(Func), static_cast(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() & { return Helper::template Construct(static_cast< TTuple& >(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() const & { return Helper::template Construct(static_cast(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() volatile& { return Helper::template Construct(static_cast< volatile TTuple& >(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() const volatile& { return Helper::template Construct(static_cast(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() && { return Helper::template Construct(static_cast< TTuple&&>(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() const && { return Helper::template Construct(static_cast(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() volatile&& { return Helper::template Construct(static_cast< volatile TTuple&&>(*this)); } - template requires (CConstructibleFrom) FORCEINLINE constexpr T Construct() const volatile&& { return Helper::template Construct(static_cast(*this)); } - - friend FORCEINLINE constexpr size_t GetTypeHash(const TTuple& A) requires (true && ... && CHashable) + /** Constructs an object of type T with a tuple as an argument. */ + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() & { return Helper::template Construct(static_cast< TTuple& >(*this)); } + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() const & { return Helper::template Construct(static_cast(*this)); } + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() volatile& { return Helper::template Construct(static_cast< volatile TTuple& >(*this)); } + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() const volatile& { return Helper::template Construct(static_cast(*this)); } + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() && { return Helper::template Construct(static_cast< TTuple&&>(*this)); } + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() const && { return Helper::template Construct(static_cast(*this)); } + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() volatile&& { return Helper::template Construct(static_cast< volatile TTuple&&>(*this)); } + template requires (CConstructibleFrom) NODISCARD FORCEINLINE constexpr T Construct() const volatile&& { return Helper::template Construct(static_cast(*this)); } + + /** Overloads the GetTypeHash algorithm for TTuple. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TTuple& A) requires (true && ... && CHashable) { return [&A](TIndexSequence) -> size_t { @@ -418,6 +434,7 @@ public: (TMakeIndexSequence()); } + /** Overloads the Swap algorithm for TTuple. */ friend FORCEINLINE constexpr void Swap(TTuple& A, TTuple& B) requires (true && ... && (CMoveConstructible && CSwappable)) { [&A, &B](TIndexSequence) @@ -435,18 +452,31 @@ TTuple(Ts...) -> TTuple; template using TPair = TTuple; +/** Creates a tuple object of the type defined by the argument types. */ template FORCEINLINE constexpr TTuple...> MakeTuple(Ts&&... Args) { return TTuple...>(Forward(Args)...); } +/** + * Creates a tuple of lvalue references or unpacks a tuple into individual objects. + * + * TTuple> SomeFunction(); + * + * FString Ret1; + * float Ret2; + * TArray Ret3; + * + * Tie(Ret1, Ret2, Ret3) = SomeFunction(); + */ template FORCEINLINE constexpr TTuple Tie(Ts&... Args) { return TTuple(Args...); } +/** Creates a tuple of forwarding references. */ template FORCEINLINE constexpr TTuple ForwardAsTuple(Ts&&... Args) { @@ -552,6 +582,7 @@ NAMESPACE_PRIVATE_END template requires (true && ... && CTTuple>) using TTupleCatResult = typename NAMESPACE_PRIVATE::TTupleCatResultImpl..., NAMESPACE_PRIVATE::FTupleEndFlag>::Type;; +/** Creates a tuple by concatenating any number of tuples. */ template requires (true && ... && CTTuple>) FORCEINLINE constexpr decltype(auto) TupleCat(TTupleTypes&&... Args) { @@ -560,6 +591,21 @@ FORCEINLINE constexpr decltype(auto) TupleCat(TTupleTypes&&... Args) else return NAMESPACE_PRIVATE::TTupleCatImpl::Do(Forward(Args)...); } +/** + * Visits each element in the specified tuples in parallel and applies them as arguments to the function. + * + * @param Func - The function to apply. + * @param Tuples - The tuples whose elements are to be applied to the function. + * + * void SomeFunction(const TTuple& TupleA, const TTuple& TupleB) + * { + * // Equivalent to: + * // Func(TupleA.Get<0>(), TupleB.Get<0>()); + * // Func(TupleA.Get<1>(), TupleB.Get<1>()); + * // Func(TupleA.Get<2>(), TupleB.Get<2>()); + * VisitTuple(Func, TupleA, TupleB); + * } + */ template requires (CTTuple> && (true && ... && CTTuple>)) FORCEINLINE constexpr void VisitTuple(F&& Func, FirstTupleType&& FirstTuple, TupleTypes&&... Tuples) @@ -587,7 +633,7 @@ NAMESPACE_REDCRAFT_END NAMESPACE_STD_BEGIN -// Support structure binding, should not be directly used +// Support structure binding, should not be directly used. template struct tuple_size> : integral_constant>> { }; template struct tuple_element> { using type = NAMESPACE_REDCRAFT::TTupleElement>; }; @@ -597,7 +643,7 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) -// Support structure binding, should not be directly used +// Support structure binding, should not be directly used. template FORCEINLINE constexpr decltype(auto) get( TTuple& InValue) { return static_cast< TTuple& >(InValue).template GetValue(); } template FORCEINLINE constexpr decltype(auto) get(const TTuple& InValue) { return static_cast& >(InValue).template GetValue(); } template FORCEINLINE constexpr decltype(auto) get( volatile TTuple& InValue) { return static_cast< volatile TTuple& >(InValue).template GetValue(); } diff --git a/Redcraft.Utility/Source/Public/Templates/TypeHash.h b/Redcraft.Utility/Source/Public/Templates/TypeHash.h index 58ce976..230cac7 100644 --- a/Redcraft.Utility/Source/Public/Templates/TypeHash.h +++ b/Redcraft.Utility/Source/Public/Templates/TypeHash.h @@ -9,16 +9,19 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) +/** Combines zero hash value. */ FORCEINLINE constexpr size_t HashCombine() { return 0; } +/** Combines one hash value to get itself. */ FORCEINLINE constexpr size_t HashCombine(size_t A) { return A; } +/** Combines two hash values to get a third. this function is not commutative. */ FORCEINLINE constexpr size_t HashCombine(size_t A, size_t C) { @@ -39,6 +42,7 @@ FORCEINLINE constexpr size_t HashCombine(size_t A, size_t C) return C; } +/** Combines more hash values to get a new value. this function is not commutative. */ template requires (true && ... && CConvertibleTo) FORCEINLINE constexpr size_t HashCombine(size_t A, size_t C, Ts... InOther) { @@ -46,6 +50,7 @@ FORCEINLINE constexpr size_t HashCombine(size_t A, size_t C, Ts... InOther) return HashCombine(B, InOther...); } +/** Overloads the GetTypeHash algorithm for CIntegral. */ template FORCEINLINE constexpr size_t GetTypeHash(T A) { @@ -59,6 +64,7 @@ FORCEINLINE constexpr size_t GetTypeHash(T A) return INDEX_NONE; } +/** Overloads the GetTypeHash algorithm for CFloatingPoint. */ template FORCEINLINE constexpr size_t GetTypeHash(T A) { @@ -74,18 +80,21 @@ FORCEINLINE constexpr size_t GetTypeHash(T A) return INDEX_NONE; } +/** Overloads the GetTypeHash algorithm for CEnum. */ template FORCEINLINE constexpr size_t GetTypeHash(T A) { return GetTypeHash(static_cast>(A)); } +/** Overloads the GetTypeHash algorithm for CPointer. */ template requires (CPointer || CSameAs) FORCEINLINE constexpr size_t GetTypeHash(T A) { return GetTypeHash(reinterpret_cast(A)); } +/** Overloads the GetTypeHash algorithm for T::hash_code(). */ template requires (requires(const T& A) { { GetTypeHash(A.hash_code()) } -> CSameAs; }) FORCEINLINE constexpr size_t GetTypeHash(const T& A) { diff --git a/Redcraft.Utility/Source/Public/Templates/Utility.h b/Redcraft.Utility/Source/Public/Templates/Utility.h index ac63632..0a57db4 100644 --- a/Redcraft.Utility/Source/Public/Templates/Utility.h +++ b/Redcraft.Utility/Source/Public/Templates/Utility.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "CoreTypes.h" #include "TypeTraits/CompositeType.h" @@ -9,15 +9,18 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) +/** Forms lvalue reference to const type of 'Ref'. */ template FORCEINLINE constexpr const T& AsConst(T& Ref) { return Ref; } +/** The const rvalue reference overload is deleted to disallow rvalue arguments. */ template void AsConst(const T&& Ref) = delete; +/** MoveTemp will cast a reference to an rvalue reference. */ template FORCEINLINE constexpr TRemoveReference&& MoveTemp(T&& Obj) { @@ -25,36 +28,43 @@ FORCEINLINE constexpr TRemoveReference&& MoveTemp(T&& Obj) return static_cast(Obj); } +/** CopyTemp will enforce the creation of an rvalue which can bind to rvalue reference parameters. */ template FORCEINLINE constexpr T CopyTemp(T& Obj) { return const_cast(Obj); } +/** CopyTemp will enforce the creation of an rvalue which can bind to rvalue reference parameters. */ template FORCEINLINE constexpr T CopyTemp(const T& Obj) { return Obj; } +/** CopyTemp will enforce the creation of an rvalue which can bind to rvalue reference parameters. */ template FORCEINLINE constexpr T&& CopyTemp(T&& Obj) { + // If we already have an rvalue, just return it unchanged, rather than needlessly creating yet another rvalue from it. return MoveTemp(Obj); } +/** Forwards lvalues as either lvalues or as rvalues, depending on T. */ template FORCEINLINE constexpr T&& Forward(TRemoveReference& Obj) { return static_cast(Obj); } +/** Forwards lvalues as either lvalues or as rvalues, depending on T. */ template FORCEINLINE constexpr T&& Forward(TRemoveReference&& Obj) { return static_cast(Obj); } +/** Exchanges the given values. */ template requires (CMoveConstructible && CMoveAssignable) FORCEINLINE constexpr void Swap(T& A, T& B) { @@ -63,6 +73,7 @@ FORCEINLINE constexpr void Swap(T& A, T& B) B = MoveTemp(Temp); } +/** Replaces the value of 'A' with 'B' and returns the old value of 'A'. */ template requires (CMoveConstructible && CAssignableFrom) FORCEINLINE constexpr T Exchange(T& A, U&& B) { @@ -71,27 +82,42 @@ FORCEINLINE constexpr T Exchange(T& A, U&& B) return Temp; } +/** + * Converts any type T to a reference type, making it possible to use member functions + * in decltype expressions without the need to go through constructors. + */ template TAddRValueReference DeclVal(); +/** Obtains the actual address of the object or function arg, even in presence of overloaded operator&. */ template requires (CObject) FORCEINLINE constexpr T* AddressOf(T& Object) { return reinterpret_cast(&const_cast(reinterpret_cast(Object))); } +/** Obtains the actual address of the object or function arg, even in presence of overloaded operator&. */ template requires (!CObject) FORCEINLINE constexpr T* AddressOf(T& Object) { return &Object; } +/** Rvalue overload is deleted to prevent taking the address of const rvalues. */ +template +const T* AddressOf(const T&&) = delete; + struct FIgnore { template FORCEINLINE constexpr void operator=(T&&) const { } }; +/** + * An object of unspecified type such that any value can be assigned to it with no effect. + * Intended for use with Tie when unpacking a TTuple, as placeholders for unused arguments + * or using Ignore to avoid warnings about unused return values ​​from NODISCARD functions. + */ inline constexpr FIgnore Ignore; // This macro is used in place of using type aliases, see Atomic.h, etc @@ -109,14 +135,15 @@ inline constexpr FIgnore Ignore; \ } -// TOverloaded Usage Example -// -// Visit(TOverloaded { -// [](auto A) { ... }, -// [](double A) { ... }, -// [](const FString& A) { ... }, -// }, Target); -// +/** + * This class is used to create a set of overloaded functions. + * + * Visit(TOverloaded { + * [](auto A) { ... }, + * [](double A) { ... }, + * [](const FString& A) { ... }, + * }, Target); + */ template struct TOverloaded : Ts... { diff --git a/Redcraft.Utility/Source/Public/Templates/Variant.h b/Redcraft.Utility/Source/Public/Templates/Variant.h index 5ad8ef3..5cf09e4 100644 --- a/Redcraft.Utility/Source/Public/Templates/Variant.h +++ b/Redcraft.Utility/Source/Public/Templates/Variant.h @@ -79,60 +79,83 @@ inline constexpr size_t TVariantIndex = NAMESPACE_PRIVATE::TVariantIndexImpl using TVariantAlternative = TCopyCV>::Type>; +/** + * The class template TVariant represents a type-safe union. An instance of TVariant + * holds a value of one of its alternative types, or in the case of invalid - no value. + */ template requires (sizeof...(Ts) > 0 && (true && ... && CDestructible)) class TVariant { public: + /** Constructs an invalid object. */ FORCEINLINE constexpr TVariant() : TypeIndex(0xFF) { }; + /** Constructs an invalid object. */ FORCEINLINE constexpr TVariant(FInvalid) : TVariant() { }; + /** Copies content of other into a new instance. */ FORCEINLINE constexpr TVariant(const TVariant& InValue) requires (true && ... && CTriviallyCopyConstructible) = default; + /** Copies content of other into a new instance. */ FORCEINLINE constexpr TVariant(const TVariant& InValue) requires ((true && ... && CCopyConstructible) && !(true && ... && CTriviallyCopyConstructible)) : TypeIndex(static_cast(InValue.GetIndex())) { if (IsValid()) CopyConstructImpl[InValue.GetIndex()](&Value, &InValue.Value); } + /** Moves content of other into a new instance. */ FORCEINLINE constexpr TVariant(TVariant&& InValue) requires (true && ... && CTriviallyMoveConstructible) = default; + /** Moves content of other into a new instance. */ FORCEINLINE constexpr TVariant(TVariant&& InValue) requires ((true && ... && CMoveConstructible) && !(true && ... && CTriviallyMoveConstructible)) : TypeIndex(static_cast(InValue.GetIndex())) { if (IsValid()) MoveConstructImpl[InValue.GetIndex()](&Value, &InValue.Value); } - - template requires (I < sizeof...(Ts) - && CConstructibleFrom>, ArgTypes...>) - FORCEINLINE constexpr explicit TVariant(TInPlaceIndex, ArgTypes&&... Args) - : TypeIndex(I) - { - using SelectedType = TVariantAlternative>; - new (&Value) SelectedType(Forward(Args)...); - } - - template requires (CConstructibleFrom) - FORCEINLINE constexpr explicit TVariant(TInPlaceType, ArgTypes&&... Args) - : TVariant(InPlaceIndex>>, Forward(Args)...) - { } - + + /** + * Converting constructor. Constructs a variant holding the alternative type that would be selected + * by overload resolution for the expression F(Forward(InValue)) if there was an overload of + * imaginary function F(T) for every T from Ts... in scope at the same time, except that an overload F(T) + * is only considered if the declaration T X[] = { Forward(InValue) }; is valid for some invented variable x. + * Direct-initializes the contained value as if by direct non-list-initialization from Forward(InValue). + */ template requires (requires { typename NAMESPACE_PRIVATE::TVariantSelectedType; } && !CTInPlaceType> && !CTInPlaceIndex> && !CSameAs>) FORCEINLINE constexpr TVariant(T&& InValue) : TVariant(InPlaceType>, Forward(InValue)) { } + + /** Constructs a variant with the specified alternative T and initializes the contained value with the arguments Forward(Args).... */ + template requires (CConstructibleFrom) + FORCEINLINE constexpr explicit TVariant(TInPlaceType, Us&&... Args) + : TVariant(InPlaceIndex>>, Forward(Args)...) + { } + /** Constructs a variant with the alternative T specified by the index I and initializes the contained value with the arguments Forward(Args).... */ + template requires (I < sizeof...(Ts) + && CConstructibleFrom>, Us...>) + FORCEINLINE constexpr explicit TVariant(TInPlaceIndex, Us&&... Args) + : TypeIndex(I) + { + using SelectedType = TVariantAlternative>; + new (&Value) SelectedType(Forward(Args)...); + } + + /** Destroys the contained object, if any, as if by a call to Reset(). */ FORCEINLINE constexpr ~TVariant() requires (true && ... && CTriviallyDestructible) = default; + /** Destroys the contained object, if any, as if by a call to Reset(). */ FORCEINLINE constexpr ~TVariant() requires (!(true && ... && CTriviallyDestructible)) { Reset(); } + /** Assigns by copying the state of 'InValue'. */ FORCEINLINE constexpr TVariant& operator=(const TVariant& InValue) requires (true && ... && (CTriviallyCopyConstructible && CTriviallyCopyAssignable)) = default; + /** Assigns by copying the state of 'InValue'. */ constexpr TVariant& operator=(const TVariant& InValue) requires ((true && ... && (CCopyConstructible && CCopyAssignable)) && !(true && ... && (CTriviallyCopyConstructible && CTriviallyCopyAssignable))) { @@ -155,8 +178,10 @@ public: return *this; } + /** Assigns by moving the state of 'InValue'. */ FORCEINLINE constexpr TVariant& operator=(TVariant&& InValue) requires (true && ... && (CTriviallyMoveConstructible && CTriviallyMoveAssignable)) = default; + /** Assigns by moving the state of 'InValue'. */ constexpr TVariant& operator=(TVariant&& InValue) requires ((true && ... && (CMoveConstructible && CMoveAssignable)) && !(true && ... && (CTriviallyMoveConstructible && CTriviallyMoveAssignable))) { @@ -179,6 +204,7 @@ public: return *this; } + /** Converting assignment. Constructs a variant holding the alternative type that would be selected by overload resolution. */ template requires (requires { typename NAMESPACE_PRIVATE::TVariantSelectedType; }) FORCEINLINE constexpr TVariant& operator=(T&& InValue) { @@ -194,8 +220,9 @@ public: return *this; } - - friend constexpr bool operator==(const TVariant& LHS, const TVariant& RHS) requires (true && ... && CEqualityComparable) + + /** Check if the two variants are equivalent. */ + NODISCARD friend constexpr bool operator==(const TVariant& LHS, const TVariant& RHS) requires (true && ... && CEqualityComparable) { if (LHS.GetIndex() != RHS.GetIndex()) return false; if (LHS.IsValid() == false) return true; @@ -206,7 +233,8 @@ public: return CompareImpl[LHS.GetIndex()](&LHS.Value, &RHS.Value); } - friend constexpr partial_ordering operator<=>(const TVariant& LHS, const TVariant& RHS) requires (true && ... && CSynthThreeWayComparable) + /** Check the order relationship between two variants. */ + NODISCARD friend constexpr partial_ordering operator<=>(const TVariant& LHS, const TVariant& RHS) requires (true && ... && CSynthThreeWayComparable) { if (LHS.GetIndex() != RHS.GetIndex()) return partial_ordering::unordered; if (LHS.IsValid() == false) return partial_ordering::equivalent; @@ -216,65 +244,87 @@ public: return CompareImpl[LHS.GetIndex()](&LHS.Value, &RHS.Value); } - + + /** Check if the variant value is equivalent to 'InValue'. */ template requires (!CSameAs && CEqualityComparable) - FORCEINLINE constexpr bool operator==(const T& InValue) const& + NODISCARD FORCEINLINE constexpr bool operator==(const T& InValue) const& { return HoldsAlternative() ? GetValue() == InValue : false; } - + + /** Check that the variant value is in ordered relationship with 'InValue'. */ template requires (!CSameAs && CEqualityComparable) - FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const& + NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const& { return HoldsAlternative() ? SynthThreeWayCompare(GetValue(), InValue) : partial_ordering::unordered; } - - FORCEINLINE constexpr bool operator==(FInvalid) const& { return !IsValid(); } - template requires (I < sizeof...(Ts) - && CConstructibleFrom>, ArgTypes...>) - FORCEINLINE constexpr TVariantAlternative>& Emplace(ArgTypes&&... Args) + /** @return true if instance does not contain a value, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool operator==(FInvalid) const& { return !IsValid(); } + + /** Equivalent to Emplace(Forward(Args)...), where I is the zero-based index of T in Types.... */ + template requires (CConstructibleFrom) + FORCEINLINE constexpr T& Emplace(Us&&... Args) + { + return Emplace>>(Forward(Args)...); + } + + /** + * First, destroys the currently contained value if any. + * Then direct-initializes the contained value as if constructing a value of type T with the arguments Forward(Args).... + * + * @param Args - The arguments to be passed to the constructor of the contained object. + * + * @return A reference to the new contained object. + */ + template requires (I < sizeof...(Ts) + && CConstructibleFrom>, Us...>) + FORCEINLINE constexpr TVariantAlternative>& Emplace(Us&&... Args) { Reset(); using SelectedType = TVariantAlternative>; - SelectedType* Result = new (&Value) SelectedType(Forward(Args)...); + SelectedType* Result = new (&Value) SelectedType(Forward(Args)...); TypeIndex = I; return *Result; } - template requires (CConstructibleFrom) - FORCEINLINE constexpr T& Emplace(ArgTypes&&... Args) - { - return Emplace>>(Forward(Args)...); - } + /** @return The typeid of the contained value if instance is non-empty, otherwise typeid(void). */ + NODISCARD FORCEINLINE constexpr const type_info& GetTypeInfo() const { return IsValid() ? *TypeInfos[GetIndex()] : typeid(void); } - FORCEINLINE constexpr const type_info& GetTypeInfo() const { return IsValid() ? *TypeInfos[GetIndex()] : typeid(void); } + /** @return The zero-based index of the alternative held by the variant. */ + NODISCARD FORCEINLINE constexpr size_t GetIndex() const { return IsValid() ? TypeIndex : INDEX_NONE; } - FORCEINLINE constexpr size_t GetIndex() const { return TypeIndex != 0xFF ? TypeIndex : INDEX_NONE; } - FORCEINLINE constexpr bool IsValid() const { return TypeIndex != 0xFF; } - FORCEINLINE constexpr explicit operator bool() const { return TypeIndex != 0xFF; } + /** @return true if instance contains a value, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool IsValid() const { return TypeIndex != 0xFF; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return TypeIndex != 0xFF; } - template FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == I : false; } - template FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == TVariantIndex> : false; } + /** @return true if the variant currently holds the alternative, false otherwise. */ + template NODISCARD FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == I : false; } + template NODISCARD FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == TVariantIndex> : false; } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< TVariantAlternative>*>(&Value); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< TVariantAlternative>*>(&Value)); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast>*>(&Value); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast>*>(&Value)); } + /** @return The contained object. */ + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< TVariantAlternative>*>(&Value); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< TVariantAlternative>*>(&Value)); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast>*>(&Value); } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast>*>(&Value)); } - template FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< T*>(&Value); } - template FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< T*>(&Value)); } - template FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast(&Value); } - template FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(&Value)); } + /** @return The contained object. */ + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< T*>(&Value); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< T*>(&Value)); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast(&Value); } + template NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(&Value)); } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) Get( TVariantAlternative>& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } - template requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) Get(const TVariantAlternative>& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } + /** @return The contained object when HoldsAlternative() returns true, 'DefaultValue' otherwise. */ + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) Get( TVariantAlternative>& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } + template requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) Get(const TVariantAlternative>& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } - template FORCEINLINE constexpr decltype(auto) Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } - template FORCEINLINE constexpr decltype(auto) Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } + /** @return The contained object when HoldsAlternative() returns true, 'DefaultValue' otherwise. */ + template NODISCARD FORCEINLINE constexpr decltype(auto) Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } + template NODISCARD FORCEINLINE constexpr decltype(auto) Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } + /** If not empty, destroys the contained object. */ FORCEINLINE constexpr void Reset() { if (GetIndex() == INDEX_NONE) return; @@ -287,7 +337,8 @@ public: TypeIndex = static_cast(INDEX_NONE); } - friend FORCEINLINE constexpr size_t GetTypeHash(const TVariant& A) requires (true && ... && CHashable) + /** Overloads the GetTypeHash algorithm for TVariant. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TVariant& A) requires (true && ... && CHashable) { if (!A.IsValid()) return 114514; @@ -297,6 +348,7 @@ public: return HashCombine(GetTypeHash(A.GetIndex()), HashImpl[A.GetIndex()](&A.Value)); } + /** Overloads the Swap algorithm for TVariant. */ friend constexpr void Swap(TVariant& A, TVariant& B) requires (true && ... && (CMoveConstructible && CSwappable)) { if (!A.IsValid() && !B.IsValid()) return; @@ -477,6 +529,7 @@ struct TVariantVisitImpl NAMESPACE_PRIVATE_END +/** Applies the visitor 'Func' (Callable that can be called with any combination of types from variants) to the variants 'Variants'. */ template requires (CTVariant> && (true && ... && CTVariant>)) constexpr decltype(auto) Visit(F&& Func, FirstVariantType&& FirstVariant, VariantTypes&&... Variants) @@ -485,6 +538,7 @@ constexpr decltype(auto) Visit(F&& Func, FirstVariantType&& FirstVariant, Varian return NAMESPACE_PRIVATE::TVariantVisitImpl::Do(Forward(Func), Forward(FirstVariant), Forward(Variants)...); } +/** Applies the visitor 'Func' (Callable that can be called with any combination of types from variants) to the variants 'Variants'. */ template requires (CTVariant> && (true && ... && CTVariant>)) constexpr Ret Visit(F&& Func, FirstVariantType&& FirstVariant, VariantTypes&&... Variants)