Compare commits
2 Commits
de059cb980
...
9368a49806
Author | SHA1 | Date | |
---|---|---|---|
9368a49806 | |||
b75cb30f4f |
@ -940,7 +940,8 @@ void TestTuple()
|
||||
always_check(C == 'A');
|
||||
}
|
||||
};
|
||||
MakeTuple(1, 1.2f, 'A').Construct<FTest>();
|
||||
|
||||
Ignore = MakeTuple(1, 1.2f, 'A').Construct<FTest>();
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -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 <typename T> requires (CIntegral<T> || CPointer<T>)
|
||||
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<uint64>(Alignment) - 1) & ~(static_cast<uint64>(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 <typename T> requires (CIntegral<T> || CPointer<T>)
|
||||
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<uint64>(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 <typename T> requires (CIntegral<T> || CPointer<T>)
|
||||
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<uint64>(Alignment) - 1) / static_cast<uint64>(Alignment)) * static_cast<uint64>(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 <typename T> requires (CIntegral<T> || CPointer<T>)
|
||||
FORCEINLINE constexpr bool IsAligned(T InValue, size_t Alignment)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
* Minimum offset between two objects to avoid false sharing.
|
||||
*
|
||||
* struct FTwoCacheLiner { // occupies two cache lines
|
||||
* alignas(DestructiveInterference) TAtomic<uint64> X;
|
||||
* alignas(DestructiveInterference) TAtomic<uint64> 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<uint64> X;
|
||||
* TAtomic<uint64> 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<uint64> X;
|
||||
* alignas(DestructiveInterference) TAtomic<uint64> 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<uint64> X;
|
||||
* TAtomic<uint64> 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 <typename T>
|
||||
/**
|
||||
* 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 <typename T> requires (!CPointer<T>)
|
||||
FORCEINLINE void Memmove(T& Destination, const T& Source)
|
||||
{
|
||||
static_assert(!CPointer<T>, "For pointers use the three parameters function");
|
||||
Memmove(&Destination, &Source, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
/**
|
||||
* 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 <typename T> requires (!CPointer<T>)
|
||||
FORCEINLINE int32 Memcmp(const T& BufferLHS, const T& BufferRHS)
|
||||
{
|
||||
static_assert(!CPointer<T>, "For pointers use the three parameters function");
|
||||
return Memcmp(&BufferLHS, &BufferRHS, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 <typename T> requires (!CPointer<T>)
|
||||
FORCEINLINE void Memset(T& Destination, uint8 ValueToSet)
|
||||
{
|
||||
static_assert(!CPointer<T>, "For pointers use the three parameters function");
|
||||
Memset(&Source, ValueToSet, sizeof(T));
|
||||
Memset(&Destination, ValueToSet, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 <typename T> requires (!CPointer<T>)
|
||||
FORCEINLINE void Memzero(T& Destination)
|
||||
{
|
||||
static_assert(!CPointer<T>, "For pointers use the two parameters function");
|
||||
Memzero(&Source, sizeof(T));
|
||||
Memzero(&Destination, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
/**
|
||||
* 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 <typename T> requires (!CPointer<T>)
|
||||
FORCEINLINE void Memcpy(T& Destination, const T& Source)
|
||||
{
|
||||
static_assert(!CPointer<T>, "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)
|
||||
|
||||
@ -118,7 +298,7 @@ NAMESPACE_REDCRAFT_END
|
||||
|
||||
// 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<NAMESPACE_REDCRAFT::size_t>(Alignment)); } \
|
||||
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<NAMESPACE_REDCRAFT::size_t>(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); }
|
||||
|
@ -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 <CDefaultConstructible ElementType>
|
||||
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 <typename DestinationElementType, typename SourceElementType = DestinationElementType>
|
||||
requires (CConstructibleFrom<DestinationElementType, const SourceElementType&>)
|
||||
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 <CCopyConstructible ElementType>
|
||||
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 <CMoveConstructible ElementType>
|
||||
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 <CCopyAssignable ElementType>
|
||||
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 <CMoveAssignable ElementType>
|
||||
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 <CDestructible ElementType>
|
||||
FORCEINLINE void Destruct(ElementType* Element, size_t Count = 1)
|
||||
{
|
||||
|
@ -27,14 +27,22 @@ concept CFAnyPlaceable = CDestructible<TDecay<T>> && CCopyConstructible<TDecay<T
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/**
|
||||
* The class any describes a type-safe container for single values of any copy and move constructible type.
|
||||
* An object of class any stores an instance of any type that satisfies the constructor requirements or is empty,
|
||||
* and this is referred to as the state of the class any object. The stored instance is called the contained object.
|
||||
*/
|
||||
class alignas(16) FAny
|
||||
{
|
||||
public:
|
||||
|
||||
/** Constructs an empty object. */
|
||||
FORCEINLINE constexpr FAny() { Invalidate(); }
|
||||
|
||||
/** Constructs an empty object. */
|
||||
FORCEINLINE constexpr FAny(FInvalid) : FAny() { }
|
||||
|
||||
/** Copies content of other into a new instance. This may use the object's copy constructor. */
|
||||
FAny(const FAny& InValue)
|
||||
: TypeInfo(InValue.TypeInfo)
|
||||
{
|
||||
@ -60,6 +68,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Moves content of other into a new instance. This may use the object's move constructor. */
|
||||
FAny(FAny&& InValue)
|
||||
: TypeInfo(InValue.TypeInfo)
|
||||
{
|
||||
@ -85,25 +94,29 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs an object with initial content an object of type TDecay<T>, direct-initialized from Forward<T>(InValue). */
|
||||
template <typename T> requires (!CSameAs<FAny, TDecay<T>> && !CTInPlaceType<TDecay<T>>
|
||||
&& NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CConstructibleFrom<TDecay<T>, T&&>)
|
||||
FORCEINLINE FAny(T&& InValue) : FAny(InPlaceType<T>, Forward<T>(InValue))
|
||||
{ }
|
||||
|
||||
/** Constructs an object with initial content an object of type TDecay<T>, direct-non-list-initialized from Forward<Ts>(Args).... */
|
||||
template <typename T, typename... Ts> requires (NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CConstructibleFrom<TDecay<T>, Ts&&...>)
|
||||
FORCEINLINE explicit FAny(TInPlaceType<T>, Ts&&... Args)
|
||||
{
|
||||
EmplaceImpl<T>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
template <typename T> requires (!CBaseOf<FAny, TDecay<T>> && !CTInPlaceType<TDecay<T>>
|
||||
&& NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CConstructibleFrom<TDecay<T>, T&&>)
|
||||
FORCEINLINE FAny(T&& InValue) : FAny(InPlaceType<TDecay<T>>, Forward<T>(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;
|
||||
}
|
||||
|
||||
template <typename T> requires (!CBaseOf<FAny, TDecay<T>> && !CTInPlaceType<TDecay<T>>
|
||||
/** Assigns the type and value of 'InValue'. This may use the object's constructor or assignment operator. */
|
||||
template <typename T> requires (!CSameAs<FAny, TDecay<T>> && !CTInPlaceType<TDecay<T>>
|
||||
&& NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CConstructibleFrom<TDecay<T>, T&&>)
|
||||
FORCEINLINE FAny& operator=(T&& InValue)
|
||||
{
|
||||
using DecayedType = TDecay<T>;
|
||||
|
||||
if constexpr (CAssignableFrom<DecayedType, T&&>)
|
||||
{
|
||||
if (HoldsAlternative<DecayedType>())
|
||||
{
|
||||
GetValue<DecayedType>() = Forward<T>(InValue);
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
Destroy();
|
||||
EmplaceImpl<DecayedType>(Forward<T>(InValue));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> requires (!CBaseOf<FAny, TRemoveCVRef<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr bool operator==(const T& InValue) const&
|
||||
/** Check if the contained value is equivalent to 'InValue'. */
|
||||
template <typename T> requires (!CSameAs<FAny, TRemoveCVRef<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CEqualityComparable<T>)
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const T& InValue) const&
|
||||
{
|
||||
return HoldsAlternative<T>() ? GetValue<T>() == InValue : false;
|
||||
}
|
||||
|
||||
template <typename T> requires (!CBaseOf<FAny, TRemoveCVRef<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const&
|
||||
/** Check that the contained value is in ordered relationship with 'InValue'. */
|
||||
template <typename T> requires (!CSameAs<FAny, TRemoveCVRef<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CSynthThreeWayComparable<T>)
|
||||
NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const&
|
||||
{
|
||||
return HoldsAlternative<T>() ? SynthThreeWayCompare(GetValue<T>(), 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<T> constructed from the arguments.
|
||||
* First destroys the current contained object (if any) by Reset(), then constructs an object of type
|
||||
* TDecay<T>, direct-non-list-initialized from Forward<Ts>(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 <typename T, typename... Ts> requires (NAMESPACE_PRIVATE::CFAnyPlaceable<T> && CConstructibleFrom<TDecay<T>, Ts&&...>)
|
||||
FORCEINLINE TDecay<T>& Emplace(Ts&&... Args)
|
||||
{
|
||||
@ -258,82 +287,68 @@ public:
|
||||
return GetValue<TDecay<T>>();
|
||||
}
|
||||
|
||||
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 <typename T> FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetTypeInfo() == typeid(T) : false; }
|
||||
/** @return true if the any currently holds the alternative 'T', false otherwise. */
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetTypeInfo() == typeid(T) : false; }
|
||||
|
||||
/** @return The contained object. */
|
||||
template <typename T> requires (CSameAs<T, TDecay<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr T& GetValue() & { checkf(HoldsAlternative<T>(), 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()); }
|
||||
NODISCARD FORCEINLINE constexpr T& GetValue() & { checkf(HoldsAlternative<T>(), 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 <typename T> requires (CSameAs<T, TDecay<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr T&& GetValue() && { checkf(HoldsAlternative<T>(), 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())); }
|
||||
NODISCARD FORCEINLINE constexpr T&& GetValue() && { checkf(HoldsAlternative<T>(), 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 <typename T> requires (CSameAs<T, TDecay<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr const T& GetValue() const& { checkf(HoldsAlternative<T>(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast<const T*>(GetStorage()); }
|
||||
NODISCARD FORCEINLINE constexpr const T& GetValue() const& { checkf(HoldsAlternative<T>(), TEXT("It is an error to call GetValue() on an wrong TAny. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast<const T*>(GetStorage()); }
|
||||
|
||||
/** @return The contained object. */
|
||||
template <typename T> requires (CSameAs<T, TDecay<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr const T&& GetValue() const&& { checkf(HoldsAlternative<T>(), 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<const T*>(GetStorage())); }
|
||||
NODISCARD FORCEINLINE constexpr const T&& GetValue() const&& { checkf(HoldsAlternative<T>(), 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<const T*>(GetStorage())); }
|
||||
|
||||
/** @return The contained object when HoldsAlternative<T>() returns true, 'DefaultValue' otherwise. */
|
||||
template <typename T> requires (CSameAs<T, TDecay<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr T& Get( T& DefaultValue) & { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
NODISCARD FORCEINLINE constexpr T& Get( T& DefaultValue) & { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
|
||||
/** @return The contained object when HoldsAlternative<T>() returns true, 'DefaultValue' otherwise. */
|
||||
template <typename T> requires (CSameAs<T, TDecay<T>> && NAMESPACE_PRIVATE::CFAnyPlaceable<T>)
|
||||
FORCEINLINE constexpr const T& Get(const T& DefaultValue) const& { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
NODISCARD FORCEINLINE constexpr const T& Get(const T& DefaultValue) const& { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
|
||||
/** If not empty, destroys the contained object. */
|
||||
FORCEINLINE void Reset()
|
||||
{
|
||||
Destroy();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void Swap(FAny& InValue)
|
||||
/** Overloads the Swap algorithm for FAny. */
|
||||
friend void Swap(FAny& A, FAny& B)
|
||||
{
|
||||
if (!IsValid() && !InValue.IsValid()) return;
|
||||
if (!A.IsValid() && !B.IsValid()) return;
|
||||
|
||||
if (IsValid() && !InValue.IsValid())
|
||||
if (A.IsValid() && !B.IsValid())
|
||||
{
|
||||
InValue = MoveTemp(*this);
|
||||
Reset();
|
||||
return;
|
||||
B = MoveTemp(A);
|
||||
A.Reset();
|
||||
}
|
||||
|
||||
if (InValue.IsValid() && !IsValid())
|
||||
else if (!A.IsValid() && B.IsValid())
|
||||
{
|
||||
*this = MoveTemp(InValue);
|
||||
InValue.Reset();
|
||||
return;
|
||||
A = MoveTemp(B);
|
||||
B.Reset();
|
||||
}
|
||||
|
||||
if (GetTypeInfo() == InValue.GetTypeInfo())
|
||||
else
|
||||
{
|
||||
switch (GetRepresentation())
|
||||
{
|
||||
case ERepresentation::Empty:
|
||||
break;
|
||||
case ERepresentation::Trivial:
|
||||
uint8 TempBuffer[sizeof(TrivialStorage.Internal)];
|
||||
Memory::Memmove(TempBuffer, TrivialStorage.Internal);
|
||||
Memory::Memmove(TrivialStorage.Internal, InValue.TrivialStorage.Internal);
|
||||
Memory::Memmove(InValue.TrivialStorage.Internal, TempBuffer);
|
||||
break;
|
||||
case ERepresentation::Small:
|
||||
SmallStorage.RTTI->SwapObject(&SmallStorage.Internal, &InValue.SmallStorage.Internal);
|
||||
break;
|
||||
case ERepresentation::Big:
|
||||
NAMESPACE_REDCRAFT::Swap(BigStorage.External, InValue.BigStorage.External);
|
||||
break;
|
||||
default: check_no_entry();
|
||||
FAny Temp = MoveTemp(A);
|
||||
A = MoveTemp(B);
|
||||
B = MoveTemp(Temp);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FAny Temp = MoveTemp(*this);
|
||||
*this = MoveTemp(InValue);
|
||||
InValue = MoveTemp(Temp);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -411,7 +426,7 @@ private:
|
||||
{
|
||||
if constexpr (CSwappable<T>)
|
||||
{
|
||||
NAMESPACE_REDCRAFT::Swap(*reinterpret_cast<T*>(A), *reinterpret_cast<T*>(B));
|
||||
Swap(*reinterpret_cast<T*>(A), *reinterpret_cast<T*>(B));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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<TUnderlyingType<NAMESPACE_STD::memory_order>>(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<T>::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<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
FORCEINLINE ValueType Load(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.load(static_cast<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
FORCEINLINE operator ValueType() const { return static_cast<ValueType>(NativeAtomic); }
|
||||
FORCEINLINE operator ValueType() const volatile requires (bIsAlwaysLockFree) { return static_cast<ValueType>(NativeAtomic); }
|
||||
/** Loads a value from an atomic object. */
|
||||
NODISCARD FORCEINLINE operator ValueType() const { return static_cast<ValueType>(NativeAtomic); }
|
||||
NODISCARD FORCEINLINE operator ValueType() const volatile requires (bIsAlwaysLockFree) { return static_cast<ValueType>(NativeAtomic); }
|
||||
|
||||
FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) { return NativeAtomic.exchange(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { return NativeAtomic.exchange(Desired, static_cast<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE ValueType Exchange(ValueType Desired, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (bIsAlwaysLockFree) { return NativeAtomic.exchange(Desired, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
FORCEINLINE bool CompareExchange(ValueType& Expected, ValueType Desired, EMemoryOrder Success, EMemoryOrder Failure, 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)
|
||||
{
|
||||
MEMORY_ORDER_CHECK(Failure, 0x01 | 0x02 | 0x04 | 0x20);
|
||||
if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Success), static_cast<NAMESPACE_STD::memory_order>(Failure));
|
||||
else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Success), static_cast<NAMESPACE_STD::memory_order>(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) volatile requires (bIsAlwaysLockFree)
|
||||
{
|
||||
MEMORY_ORDER_CHECK(Failure, 0x01 | 0x02 | 0x04 | 0x20);
|
||||
if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Success), static_cast<NAMESPACE_STD::memory_order>(Failure));
|
||||
else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Success), static_cast<NAMESPACE_STD::memory_order>(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 Order = EMemoryOrder::SequentiallyConsistent, bool bIsWeak = false)
|
||||
{
|
||||
if (bIsWeak) return NativeAtomic.compare_exchange_weak(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(Order));
|
||||
else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(Order));
|
||||
else return NativeAtomic.compare_exchange_strong(Expected, Desired, static_cast<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Wait(ValueType Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); NativeAtomic.wait(Old, static_cast<NAMESPACE_STD::memory_order>(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 <typename F> requires (CInvocableResult<ValueType, F, ValueType>)
|
||||
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 <typename F> requires (CInvocableResult<ValueType, F, ValueType> && 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<T> || CFloatingPoint<T>) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchAdd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(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<T> ) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchAdd(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_add(InValue, static_cast<NAMESPACE_STD::memory_order>(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<T> || CFloatingPoint<T>) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchSub(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(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<T> ) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchSub(ptrdiff InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_sub(InValue, static_cast<NAMESPACE_STD::memory_order>(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<T> || CFloatingPoint<T>) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old * InValue; }); }
|
||||
FORCEINLINE ValueType FetchMul(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && 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<T> || CFloatingPoint<T>) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old / InValue; }); }
|
||||
FORCEINLINE ValueType FetchDiv(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> || CFloatingPoint<T>) && 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<T> ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old % InValue; }); }
|
||||
FORCEINLINE ValueType FetchMod(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && 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<T> ) { return NativeAtomic.fetch_and(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchAnd(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_and(InValue, static_cast<NAMESPACE_STD::memory_order>(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<T> ) { return NativeAtomic.fetch_or(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchOr(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_or(InValue, static_cast<NAMESPACE_STD::memory_order>(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<T> ) { return NativeAtomic.fetch_xor(InValue, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE ValueType FetchXor(ValueType InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic.fetch_xor(InValue, static_cast<NAMESPACE_STD::memory_order>(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<T> ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old << InValue; }); }
|
||||
FORCEINLINE ValueType FetchLsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && 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<T> ) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old >> InValue; }); }
|
||||
FORCEINLINE ValueType FetchRsh(size_t InValue, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchFn([InValue](ValueType Old) -> ValueType { return Old >> InValue; }); }
|
||||
|
||||
/** Increments the atomic value by one. */
|
||||
FORCEINLINE ValueType operator++() requires ((CIntegral<T> || CPointer<T>) ) { return ++NativeAtomic; }
|
||||
FORCEINLINE ValueType operator++() volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return ++NativeAtomic; }
|
||||
|
||||
/** Increments the atomic value by one. */
|
||||
FORCEINLINE ValueType operator++(int) requires ((CIntegral<T> || CPointer<T>) ) { return NativeAtomic++; }
|
||||
FORCEINLINE ValueType operator++(int) volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return NativeAtomic++; }
|
||||
|
||||
/** Decrements the atomic value by one. */
|
||||
FORCEINLINE ValueType operator--() requires ((CIntegral<T> || CPointer<T>) ) { return --NativeAtomic; }
|
||||
FORCEINLINE ValueType operator--() volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return --NativeAtomic; }
|
||||
|
||||
/** Decrements the atomic value by one. */
|
||||
FORCEINLINE ValueType operator--(int) requires ((CIntegral<T> || CPointer<T>) ) { return NativeAtomic--; }
|
||||
FORCEINLINE ValueType operator--(int) volatile requires ((CIntegral<T> || CPointer<T>) && bIsAlwaysLockFree) { return NativeAtomic--; }
|
||||
|
||||
/** Adds with the atomic value. */
|
||||
FORCEINLINE ValueType operator+=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE ValueType operator+=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return NativeAtomic += InValue; }
|
||||
|
||||
/** Adds with the atomic value. */
|
||||
FORCEINLINE ValueType operator+=(ptrdiff InValue) requires (CPointer<T> ) { return NativeAtomic += InValue; }
|
||||
FORCEINLINE ValueType operator+=(ptrdiff InValue) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic += InValue; }
|
||||
|
||||
/** Subtracts with the atomic value. */
|
||||
FORCEINLINE ValueType operator-=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE ValueType operator-=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return NativeAtomic -= InValue; }
|
||||
|
||||
/** Subtracts with the atomic value. */
|
||||
FORCEINLINE ValueType operator-=(ptrdiff InValue) requires (CPointer<T> ) { return NativeAtomic -= InValue; }
|
||||
FORCEINLINE ValueType operator-=(ptrdiff InValue) volatile requires (CPointer<T> && bIsAlwaysLockFree) { return NativeAtomic -= InValue; }
|
||||
|
||||
/** Multiples with the atomic value. */
|
||||
FORCEINLINE ValueType operator*=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return FetchMul(InValue) * InValue; }
|
||||
FORCEINLINE ValueType operator*=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return FetchMul(InValue) * InValue; }
|
||||
|
||||
/** Divides with the atomic value. */
|
||||
FORCEINLINE ValueType operator/=(ValueType InValue) requires ((CIntegral<T> || CFloatingPoint<T>) ) { return FetchDiv(InValue) / InValue; }
|
||||
FORCEINLINE ValueType operator/=(ValueType InValue) volatile requires ((CIntegral<T> || CFloatingPoint<T>) && bIsAlwaysLockFree) { return FetchDiv(InValue) / InValue; }
|
||||
|
||||
/** Models with the atomic value. */
|
||||
FORCEINLINE ValueType operator%=(ValueType InValue) requires (CIntegral<T> ) { return FetchMod(InValue) % InValue; }
|
||||
FORCEINLINE ValueType operator%=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchMod(InValue) % InValue; }
|
||||
|
||||
/** Performs bitwise AND with the atomic value. */
|
||||
FORCEINLINE ValueType operator&=(ValueType InValue) requires (CIntegral<T> ) { return NativeAtomic &= InValue; }
|
||||
FORCEINLINE ValueType operator&=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic &= InValue; }
|
||||
|
||||
/** Performs bitwise OR with the atomic value. */
|
||||
FORCEINLINE ValueType operator|=(ValueType InValue) requires (CIntegral<T> ) { return NativeAtomic |= InValue; }
|
||||
FORCEINLINE ValueType operator|=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic |= InValue; }
|
||||
|
||||
/** Performs bitwise XOR with the atomic value. */
|
||||
FORCEINLINE ValueType operator^=(ValueType InValue) requires (CIntegral<T> ) { return NativeAtomic ^= InValue; }
|
||||
FORCEINLINE ValueType operator^=(ValueType InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return NativeAtomic ^= InValue; }
|
||||
|
||||
/** Performs bitwise LSH with the atomic value. */
|
||||
FORCEINLINE ValueType operator<<=(size_t InValue) requires (CIntegral<T> ) { return FetchLsh(InValue) << InValue; }
|
||||
FORCEINLINE ValueType operator<<=(size_t InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchLsh(InValue) << InValue; }
|
||||
|
||||
/** Performs bitwise RSH with the atomic value. */
|
||||
FORCEINLINE ValueType operator>>=(size_t InValue) requires (CIntegral<T> ) { return FetchRsh(InValue) >> InValue; }
|
||||
FORCEINLINE ValueType operator>>=(size_t InValue) volatile requires (CIntegral<T> && bIsAlwaysLockFree) { return FetchRsh(InValue) >> InValue; }
|
||||
|
||||
@ -245,24 +300,34 @@ TAtomicRef(T&) -> TAtomicRef<T>;
|
||||
|
||||
static_assert(sizeof(TAtomic<int32>) == 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<bool>, 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<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Clear(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x08 | 0x20); NativeAtomic.clear(static_cast<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE bool TestAndSet(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) volatile { return NativeAtomic.test_and_set(static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
FORCEINLINE bool Test(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.test(static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE bool Test(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.test(static_cast<NAMESPACE_STD::memory_order>(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<NAMESPACE_STD::memory_order>(Order)); }
|
||||
NODISCARD FORCEINLINE bool Test(EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); return NativeAtomic.test(static_cast<NAMESPACE_STD::memory_order>(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<const NAMESPACE_STD::atomic_flag&>(NativeAtomic).wait(Old, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
FORCEINLINE void Wait(bool Old, EMemoryOrder Order = EMemoryOrder::SequentiallyConsistent) const volatile { MEMORY_ORDER_CHECK(Order, 0x01 | 0x02 | 0x04 | 0x20); const_cast<const NAMESPACE_STD::atomic_flag&>(NativeAtomic).wait(Old, static_cast<NAMESPACE_STD::memory_order>(Order)); }
|
||||
|
||||
/** Notifies at least one or all threads blocked waiting on the atomic object. */
|
||||
FORCEINLINE void Notify(bool bIsAll = false) { if (bIsAll) const_cast<NAMESPACE_STD::atomic_flag&>(NativeAtomic).notify_all(); else const_cast<NAMESPACE_STD::atomic_flag&>(NativeAtomic).notify_one(); }
|
||||
FORCEINLINE void Notify(bool bIsAll = false) volatile { if (bIsAll) const_cast<NAMESPACE_STD::atomic_flag&>(NativeAtomic).notify_all(); else const_cast<NAMESPACE_STD::atomic_flag&>(NativeAtomic).notify_one(); }
|
||||
|
||||
@ -273,7 +338,7 @@ private:
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline T KillDependency(T InValue)
|
||||
NODISCARD inline T KillDependency(T InValue)
|
||||
{
|
||||
T Temp(InValue);
|
||||
return Temp;
|
||||
|
@ -81,12 +81,6 @@ public:
|
||||
Callable = InCallable;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr void Swap(TFunctionStorage& InValue)
|
||||
{
|
||||
NAMESPACE_REDCRAFT::Swap(ValuePtr, InValue.ValuePtr);
|
||||
NAMESPACE_REDCRAFT::Swap(Callable, InValue.Callable);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uintptr ValuePtr;
|
||||
@ -160,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())
|
||||
{
|
||||
@ -197,7 +191,7 @@ public:
|
||||
|
||||
TFunctionStorage& operator=(TFunctionStorage&& InValue)
|
||||
{
|
||||
if (&InValue == this) return *this;
|
||||
if (&InValue == this) UNLIKELY return *this;
|
||||
|
||||
if (!InValue.IsValid())
|
||||
{
|
||||
@ -295,27 +289,27 @@ public:
|
||||
|
||||
}
|
||||
|
||||
void Swap(TFunctionStorage& InValue)
|
||||
friend void Swap(TFunctionStorage& A, TFunctionStorage& B)
|
||||
{
|
||||
if (!IsValid() && !InValue.IsValid()) return;
|
||||
if (!A.IsValid() && !B.IsValid()) return;
|
||||
|
||||
if (IsValid() && !InValue.IsValid())
|
||||
if (A.IsValid() && !B.IsValid())
|
||||
{
|
||||
InValue = MoveTemp(*this);
|
||||
Destroy();
|
||||
Invalidate();
|
||||
B = MoveTemp(A);
|
||||
A.Destroy();
|
||||
A.Invalidate();
|
||||
}
|
||||
else if (InValue.IsValid() && !IsValid())
|
||||
else if (!A.IsValid() && B.IsValid())
|
||||
{
|
||||
*this = MoveTemp(InValue);
|
||||
InValue.Destroy();
|
||||
InValue.Invalidate();
|
||||
A = MoveTemp(B);
|
||||
B.Destroy();
|
||||
B.Invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
TFunctionStorage Temp = MoveTemp(*this);
|
||||
*this = MoveTemp(InValue);
|
||||
InValue = MoveTemp(Temp);
|
||||
TFunctionStorage Temp = MoveTemp(A);
|
||||
A = MoveTemp(B);
|
||||
B = MoveTemp(Temp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,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<CVRef, int >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) & requires (CSameAs<CVRef, int& >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) && requires (CSameAs<CVRef, int&&>) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
@ -482,12 +477,12 @@ public:
|
||||
FORCEINLINE ResultType operator()(Ts... Args) const& requires (CSameAs<CVRef, const int& >) { return CallImpl(Forward<Ts>(Args)...); }
|
||||
FORCEINLINE ResultType operator()(Ts... Args) const&& requires (CSameAs<CVRef, const int&&>) { return CallImpl(Forward<Ts>(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(); }
|
||||
|
||||
FORCEINLINE constexpr void Swap(TFunctionImpl& InValue) { Storage.Swap(InValue.Storage); }
|
||||
/** @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:
|
||||
|
||||
@ -502,7 +497,7 @@ private:
|
||||
return Callable(Storage.GetValuePtr(), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
protected: // These functions should not be used by user-defined class
|
||||
protected:
|
||||
|
||||
// Use Invalidate() to invalidate the storage or use Emplace<T>() to emplace a new object after destruction.
|
||||
FORCEINLINE constexpr void Destroy() { Storage.Destroy(); }
|
||||
@ -536,10 +531,23 @@ protected: // These functions should not be used by user-defined class
|
||||
return *reinterpret_cast<DecayedType*>(Storage.GetValuePtr());
|
||||
}
|
||||
|
||||
friend FORCEINLINE constexpr void Swap(TFunctionImpl& A, TFunctionImpl& B) requires (!bIsRef) { Swap(A.Storage, B.Storage); }
|
||||
|
||||
};
|
||||
|
||||
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 <CFunction F>
|
||||
class TFunctionRef
|
||||
: public NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
@ -556,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 <typename T> requires (!CTFunctionRef<TDecay<T>>
|
||||
&& NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value)
|
||||
FORCEINLINE constexpr TFunctionRef(T&& InValue)
|
||||
@ -580,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 <CFunction F>
|
||||
class TFunction
|
||||
: public NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
@ -596,6 +614,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor. */
|
||||
FORCEINLINE constexpr TFunction(nullptr_t = nullptr) { Impl::Invalidate(); }
|
||||
|
||||
FORCEINLINE TFunction(const TFunction& InValue) = default;
|
||||
@ -603,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<T>,
|
||||
* direct-initialized from Forward<T>(InValue).
|
||||
*/
|
||||
template <typename T> requires (!CTInPlaceType<TDecay<T>>
|
||||
&& !CTFunctionRef<TDecay<T>> && !CTFunction<TDecay<T>> && !CTUniqueFunction<TDecay<T>>
|
||||
&& CConstructibleFrom<TDecay<T>, T&&> && CCopyConstructible<TDecay<T>>
|
||||
@ -614,6 +637,10 @@ public:
|
||||
else Impl::template Emplace<T>(Forward<T>(InValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an TFunction with initial content an function object of type TDecay<T>,
|
||||
* direct-non-list-initialized from Forward<Ts>(Args)....
|
||||
*/
|
||||
template <typename T, typename... ArgTypes> requires (NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value
|
||||
&& CConstructibleFrom<TDecay<T>, ArgTypes...> && CCopyConstructible<TDecay<T>>
|
||||
&& CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
@ -622,8 +649,10 @@ public:
|
||||
Impl::template Emplace<T>(Forward<ArgTypes>(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 <typename T> requires (NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value
|
||||
&& !CTFunctionRef<TDecay<T>> && !CTFunction<TDecay<T>> && !CTUniqueFunction<TDecay<T>>
|
||||
&& CConstructibleFrom<TDecay<T>, T&&> && CCopyConstructible<TDecay<T>>
|
||||
@ -636,6 +665,15 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the function object to one of type TDecay<T> constructed from the arguments.
|
||||
* First destroys the current function object (if any) by Reset(), then constructs an object of type
|
||||
* TDecay<T>, direct-non-list-initialized from Forward<Ts>(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 <typename T, typename... ArgTypes> requires (NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value
|
||||
&& CConstructibleFrom<TDecay<T>, ArgTypes...> && CCopyConstructible<TDecay<T>>
|
||||
&& CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
@ -645,10 +683,20 @@ public:
|
||||
return Impl::template Emplace<T>(Forward<ArgTypes>(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<Impl&>(A), static_cast<Impl&>(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 <CFunction F>
|
||||
class TUniqueFunction
|
||||
: public NAMESPACE_PRIVATE::TFunctionImpl<
|
||||
@ -665,6 +713,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor. */
|
||||
FORCEINLINE constexpr TUniqueFunction(nullptr_t = nullptr) { Impl::Invalidate(); }
|
||||
|
||||
FORCEINLINE TUniqueFunction(const TUniqueFunction& InValue) = delete;
|
||||
@ -672,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<F>& InValue)
|
||||
{
|
||||
new (this) TFunction<F>(InValue);
|
||||
}
|
||||
|
||||
/** Constructor from TFunction to TUniqueFunction. */
|
||||
FORCEINLINE TUniqueFunction(TFunction<F>&& InValue)
|
||||
{
|
||||
new (this) TFunction<F>(MoveTemp(InValue));
|
||||
}
|
||||
|
||||
/** Assignment operator from TFunction to TUniqueFunction. */
|
||||
FORCEINLINE TUniqueFunction& operator=(const TFunction<F>& InValue)
|
||||
{
|
||||
*reinterpret_cast<TFunction<F>*>(this) = InValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assignment operator from TFunction to TUniqueFunction. */
|
||||
FORCEINLINE TUniqueFunction& operator=(TFunction<F>&& InValue)
|
||||
{
|
||||
*reinterpret_cast<TFunction<F>*>(this) = MoveTemp(InValue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an TUniqueFunction with initial content an function object of type TDecay<T>,
|
||||
* direct-initialized from Forward<T>(InValue).
|
||||
*/
|
||||
template <typename T> requires (!CTInPlaceType<TDecay<T>>
|
||||
&& !CTFunctionRef<TDecay<T>> && !CTFunction<TDecay<T>> && !CTUniqueFunction<TDecay<T>>
|
||||
&& CConstructibleFrom<TDecay<T>, T&&> && CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>
|
||||
@ -704,6 +761,10 @@ public:
|
||||
else Impl::template Emplace<T>(Forward<T>(InValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an TUniqueFunction with initial content an function object of type TDecay<T>,
|
||||
* direct-non-list-initialized from Forward<Ts>(Args)....
|
||||
*/
|
||||
template <typename T, typename... ArgTypes> requires (NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value
|
||||
&& CConstructibleFrom<TDecay<T>, ArgTypes...> && CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE explicit TUniqueFunction(TInPlaceType<T>, ArgTypes&&... Args)
|
||||
@ -711,6 +772,7 @@ public:
|
||||
Impl::template Emplace<T>(Forward<ArgTypes>(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 <typename T> requires (NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value
|
||||
@ -724,6 +786,15 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the function object to one of type TDecay<T> constructed from the arguments.
|
||||
* First destroys the current function object (if any) by Reset(), then constructs an object of type
|
||||
* TDecay<T>, direct-non-list-initialized from Forward<Ts>(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 <typename T, typename... ArgTypes> requires (NAMESPACE_PRIVATE::TIsInvocableSignature<F, TDecay<T>>::Value
|
||||
&& CConstructibleFrom<TDecay<T>, ArgTypes...> && CMoveConstructible<TDecay<T>> && CDestructible<TDecay<T>>)
|
||||
FORCEINLINE TDecay<T>& Emplace(ArgTypes&&... Args)
|
||||
@ -733,8 +804,12 @@ public:
|
||||
return Impl::template Emplace<T>(Forward<ArgTypes>(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<Impl&>(A), static_cast<Impl&>(B)); }
|
||||
|
||||
};
|
||||
|
||||
static_assert(sizeof(TFunction<void()>) == 64, "The byte size of TFunction is unexpected");
|
||||
@ -781,8 +856,9 @@ struct TNotFunction
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename F> requires (CConstructibleFrom<F, F&&>)
|
||||
FORCEINLINE constexpr NAMESPACE_PRIVATE::TNotFunction<TDecay<F>> NotFn(F&& Func)
|
||||
/** Creates a forwarding call wrapper that returns the negation of the callable object it holds. */
|
||||
template <typename F> requires (CConstructibleFrom<F, F&&> && CMoveConstructible<F>)
|
||||
NODISCARD FORCEINLINE constexpr NAMESPACE_PRIVATE::TNotFunction<TDecay<F>> NotFn(F&& Func)
|
||||
{
|
||||
return { Forward<F>(Func) };
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ struct InvokeImpl<F, T, Ts...> : InvokeMember<F, T> { };
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
/** Invoke the Callable object f with the parameters args. */
|
||||
template <typename F, typename... Ts> requires (CInvocable<F, Ts...>)
|
||||
FORCEINLINE constexpr auto Invoke(F&& Func, Ts&&... Args)
|
||||
-> decltype(NAMESPACE_PRIVATE::InvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...))
|
||||
@ -88,8 +89,9 @@ FORCEINLINE constexpr auto Invoke(F&& Func, Ts&&... Args)
|
||||
return NAMESPACE_PRIVATE::InvokeImpl<F, Ts...>::Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/** Invoke the Callable object f with the parameters args. */
|
||||
template <typename R, typename F, typename... Ts> requires (CInvocableResult<R, F, Ts...>)
|
||||
FORCEINLINE constexpr R InvokeResult(F&& Func, Ts&&... Args)
|
||||
NODISCARD FORCEINLINE constexpr R InvokeResult(F&& Func, Ts&&... Args)
|
||||
{
|
||||
if constexpr (CVoid<R>) Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
|
||||
else return Invoke(Forward<F>(Func), Forward<Ts>(Args)...);
|
||||
|
@ -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;
|
||||
|
@ -38,6 +38,7 @@ NAMESPACE_PRIVATE_END
|
||||
|
||||
template <typename T> concept CTOptional = NAMESPACE_PRIVATE::TIsTOptional<TRemoveCV<T>>::Value;
|
||||
|
||||
/** The class template manages an optional contained value, i.e. a value that may or may not be present. */
|
||||
template <typename OptionalType> requires (CDestructible<OptionalType>)
|
||||
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<T>(InValue). */
|
||||
template <typename T = OptionalType> requires (CConstructibleFrom<OptionalType, T&&>)
|
||||
&& (!CSameAs<TRemoveCVRef<T>, FInPlace>) && (!CSameAs<TOptional, TRemoveCVRef<T>>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<T&&, OptionalType>) TOptional(T&& InValue)
|
||||
: TOptional(InPlace, Forward<T>(InValue))
|
||||
{ }
|
||||
|
||||
/** Constructs an object with initial content an object, direct-non-list-initialized from Forward<Ts>(Args).... */
|
||||
template <typename... Ts> requires (CConstructibleFrom<OptionalType, Ts...>)
|
||||
FORCEINLINE constexpr explicit TOptional(FInPlace, Ts&&... Args)
|
||||
: bIsValid(true)
|
||||
@ -56,28 +67,27 @@ public:
|
||||
new (&Value) OptionalType(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
template <typename T = OptionalType> requires (CConstructibleFrom<OptionalType, T&&>)
|
||||
&& (!CSameAs<TRemoveCVRef<T>, FInPlace>) && (!CBaseOf<TOptional, TRemoveCVRef<T>>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<T&&, OptionalType>) TOptional(T&& InValue)
|
||||
: TOptional(InPlace, Forward<T>(InValue))
|
||||
{ }
|
||||
|
||||
/** Copies content of other into a new instance. */
|
||||
FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CTriviallyCopyConstructible<OptionalType>) = default;
|
||||
|
||||
/** Copies content of other into a new instance. */
|
||||
FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CCopyConstructible<OptionalType> && !CTriviallyCopyConstructible<OptionalType>)
|
||||
: 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<OptionalType>) = default;
|
||||
|
||||
/** Moves content of other into a new instance. */
|
||||
FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CMoveConstructible<OptionalType> && !CTriviallyMoveConstructible<OptionalType>)
|
||||
: bIsValid(InValue.IsValid())
|
||||
{
|
||||
if (InValue.IsValid()) new (&Value) OptionalType(MoveTemp(InValue.GetValue()));
|
||||
}
|
||||
|
||||
/** Converting copy constructor. */
|
||||
template <typename T = OptionalType> requires (CConstructibleFrom<OptionalType, const T&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<T, OptionalType>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<const T&, OptionalType>) TOptional(const TOptional<T>& InValue)
|
||||
: bIsValid(InValue.IsValid())
|
||||
@ -85,6 +95,7 @@ public:
|
||||
if (InValue.IsValid()) new (&Value) OptionalType(InValue.GetValue());
|
||||
}
|
||||
|
||||
/** Converting move constructor. */
|
||||
template <typename T = OptionalType> requires (CConstructibleFrom<OptionalType, T&&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<T, OptionalType>)
|
||||
FORCEINLINE constexpr explicit (!CConvertibleTo<T&&, OptionalType>) TOptional(TOptional<T>&& 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<OptionalType>) = default;
|
||||
|
||||
/** Destroys the contained object, if any, as if by a call to Reset(). */
|
||||
FORCEINLINE constexpr ~TOptional() requires (!CTriviallyDestructible<OptionalType>)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
/** Assigns by copying the state of 'InValue'. */
|
||||
FORCEINLINE constexpr TOptional& operator=(const TOptional& InValue) requires (CTriviallyCopyConstructible<OptionalType> && CTriviallyCopyAssignable<OptionalType>) = default;
|
||||
|
||||
/** Assigns by copying the state of 'InValue'. */
|
||||
constexpr TOptional& operator=(const TOptional& InValue) requires (CCopyConstructible<OptionalType> && CCopyAssignable<OptionalType>
|
||||
&& !CTriviallyCopyConstructible<OptionalType> && !CTriviallyCopyAssignable<OptionalType>)
|
||||
{
|
||||
@ -122,8 +137,10 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assigns by moving the state of 'InValue'. */
|
||||
FORCEINLINE constexpr TOptional& operator=(TOptional&& InValue) requires (CTriviallyMoveConstructible<OptionalType> && CTriviallyMoveAssignable<OptionalType>) = default;
|
||||
|
||||
/** Assigns by moving the state of 'InValue'. */
|
||||
constexpr TOptional& operator=(TOptional&& InValue) requires (CMoveConstructible<OptionalType> && CMoveAssignable<OptionalType>
|
||||
&& !CTriviallyMoveConstructible<OptionalType> && !CTriviallyMoveAssignable<OptionalType>)
|
||||
{
|
||||
@ -145,6 +162,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assigns by copying the state of 'InValue'. */
|
||||
template <typename T = OptionalType> requires (CConstructibleFrom<OptionalType, const T&>
|
||||
&& CAssignableFrom<OptionalType&, const T&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<T, OptionalType>)
|
||||
constexpr TOptional& operator=(const TOptional<T>& InValue)
|
||||
@ -165,6 +183,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assigns by moving the state of 'InValue'. */
|
||||
template <typename T = OptionalType> requires (CConstructibleFrom<OptionalType, T&&>
|
||||
&& CAssignableFrom<OptionalType&, T&&> && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable<T, OptionalType>)
|
||||
constexpr TOptional& operator=(TOptional<T>&& InValue)
|
||||
@ -185,6 +204,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assigns the value of 'InValue'. */
|
||||
template <typename T = OptionalType> requires (CConstructibleFrom<OptionalType, T&&> && CAssignableFrom<OptionalType&, T&&>)
|
||||
FORCEINLINE constexpr TOptional& operator=(T&& InValue)
|
||||
{
|
||||
@ -198,36 +218,50 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Check if the two optional are equivalent. */
|
||||
template <typename T = OptionalType> requires (CWeaklyEqualityComparable<OptionalType, T>)
|
||||
friend FORCEINLINE constexpr bool operator==(const TOptional& LHS, const TOptional<T>& RHS)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TOptional& LHS, const TOptional<T>& 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 <typename T = OptionalType> requires (CSynthThreeWayComparable<OptionalType, T>)
|
||||
friend FORCEINLINE constexpr partial_ordering operator<=>(const TOptional& LHS, const TOptional<T>& RHS)
|
||||
NODISCARD friend FORCEINLINE constexpr partial_ordering operator<=>(const TOptional& LHS, const TOptional<T>& 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 <typename T = OptionalType> requires (!CTOptional<T>&& CWeaklyEqualityComparable<OptionalType, T>)
|
||||
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 <typename T = OptionalType> requires (!CTOptional<T>&& CSynthThreeWayComparable<OptionalType, T>)
|
||||
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<Ts>(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 <typename... ArgTypes> requires (CConstructibleFrom<OptionalType, ArgTypes...>)
|
||||
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; }
|
||||
/** @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 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<const 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 MoveTemp(*reinterpret_cast<const OptionalType*>(&Value)); }
|
||||
/** @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<const 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 MoveTemp(*reinterpret_cast<const OptionalType*>(&Value)); }
|
||||
|
||||
FORCEINLINE constexpr const OptionalType* operator->() const { return &GetValue(); }
|
||||
FORCEINLINE constexpr OptionalType* operator->() { 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& 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 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(); }
|
||||
|
||||
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 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,32 +309,32 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr size_t GetTypeHash() const requires (CHashable<OptionalType>)
|
||||
/** Overloads the GetTypeHash algorithm for TOptional. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TOptional& A) requires (CHashable<OptionalType>)
|
||||
{
|
||||
if (!IsValid()) return 2824517378;
|
||||
return NAMESPACE_REDCRAFT::GetTypeHash(GetValue());
|
||||
if (!A.IsValid()) return 2824517378;
|
||||
return GetTypeHash(A.GetValue());
|
||||
}
|
||||
|
||||
template <typename T> requires (CMoveConstructible<OptionalType> && CSwappable<OptionalType>)
|
||||
constexpr void Swap(TOptional& InValue)
|
||||
/** Overloads the Swap algorithm for TOptional. */
|
||||
friend constexpr void Swap(TOptional& A, TOptional& B) requires (CMoveConstructible<OptionalType> && CSwappable<OptionalType>)
|
||||
{
|
||||
if (!IsValid() && !InValue.IsValid()) return;
|
||||
if (!A.IsValid() && !B.IsValid()) return;
|
||||
|
||||
if (IsValid() && !InValue.IsValid())
|
||||
if (A.IsValid() && !B.IsValid())
|
||||
{
|
||||
InValue = MoveTemp(*this);
|
||||
Reset();
|
||||
return;
|
||||
B = MoveTemp(A);
|
||||
A.Reset();
|
||||
}
|
||||
|
||||
if (InValue.IsValid() && !IsValid())
|
||||
else if (!A.IsValid() && B.IsValid())
|
||||
{
|
||||
*this = MoveTemp(InValue);
|
||||
InValue.Reset();
|
||||
return;
|
||||
A = MoveTemp(B);
|
||||
B.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Swap(A.GetValue(), B.GetValue());
|
||||
}
|
||||
|
||||
NAMESPACE_REDCRAFT::Swap(GetValue(), InValue.GetValue());
|
||||
}
|
||||
|
||||
private:
|
||||
@ -307,20 +347,23 @@ private:
|
||||
template <typename T>
|
||||
TOptional(T) -> TOptional<T>;
|
||||
|
||||
/** Creates an optional object that does not contain a value. */
|
||||
template <typename T> requires (CDestructible<T>)
|
||||
FORCEINLINE constexpr TOptional<TDecay<T>> MakeOptional(FInvalid)
|
||||
NODISCARD FORCEINLINE constexpr TOptional<TDecay<T>> MakeOptional(FInvalid)
|
||||
{
|
||||
return TOptional<TDecay<T>>(Invalid);
|
||||
}
|
||||
|
||||
/** Creates an optional object from value. */
|
||||
template <typename T> requires (CDestructible<T> && CConstructibleFrom<T, T&&>)
|
||||
FORCEINLINE constexpr TOptional<T> MakeOptional(T&& InValue)
|
||||
NODISCARD FORCEINLINE constexpr TOptional<T> MakeOptional(T&& InValue)
|
||||
{
|
||||
return TOptional<T>(Forward<T>(InValue));
|
||||
}
|
||||
|
||||
/** Creates an optional object constructed in-place from args.... */
|
||||
template <typename T, typename... Ts> requires (CDestructible<T> && CConstructibleFrom<T, Ts...>)
|
||||
FORCEINLINE constexpr TOptional<T> MakeOptional(Ts&&... Args)
|
||||
NODISCARD FORCEINLINE constexpr TOptional<T> MakeOptional(Ts&&... Args)
|
||||
{
|
||||
return TOptional<T>(InPlace, Forward<T>(Args)...);
|
||||
}
|
||||
|
@ -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 <typename ReferencedType> requires (CObject<ReferencedType> || CFunction<ReferencedType>)
|
||||
class TReferenceWrapper
|
||||
{
|
||||
@ -17,6 +22,7 @@ public:
|
||||
|
||||
using Type = ReferencedType;
|
||||
|
||||
/** Constructs a new reference wrapper. */
|
||||
template <typename T = ReferencedType> requires (CConvertibleTo<T, ReferencedType&>)
|
||||
FORCEINLINE constexpr TReferenceWrapper(T&& Object)
|
||||
{
|
||||
@ -24,32 +30,45 @@ 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 <typename T = ReferencedType> requires (CConvertibleTo<T&, ReferencedType&>)
|
||||
FORCEINLINE constexpr TReferenceWrapper(const TReferenceWrapper<T>& InValue)
|
||||
: Pointer(InValue.Pointer)
|
||||
{ }
|
||||
|
||||
/** Assign a value to the referenced object. */
|
||||
template <typename T = ReferencedType> requires (CAssignableFrom<ReferencedType&, T&&>)
|
||||
FORCEINLINE constexpr TReferenceWrapper& operator=(T&& Object) { Get() = Forward<T>(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 <typename... Ts>
|
||||
FORCEINLINE constexpr TInvokeResult<ReferencedType&, Ts...> operator()(Ts&&... Args) const
|
||||
{
|
||||
return Invoke(Get(), Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr size_t GetTypeHash() const requires (CHashable<ReferencedType>)
|
||||
/** Overloads the GetTypeHash algorithm for TReferenceWrapper. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(TReferenceWrapper A) requires (CHashable<ReferencedType>)
|
||||
{
|
||||
return NAMESPACE_REDCRAFT::GetTypeHash(Get());
|
||||
return GetTypeHash(A.Get());
|
||||
}
|
||||
|
||||
/** Overloads the Swap algorithm for TReferenceWrapper. */
|
||||
friend FORCEINLINE constexpr void Swap(TReferenceWrapper A, TReferenceWrapper B) requires (CSwappable<ReferencedType>)
|
||||
{
|
||||
Swap(A.Get(), B.Get());
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -176,9 +176,9 @@ protected:
|
||||
FORCEINLINE constexpr TTupleImpl& operator=(TTupleImpl&&) = default;
|
||||
FORCEINLINE constexpr ~TTupleImpl() = default;
|
||||
|
||||
template <typename... ArgTypes>
|
||||
FORCEINLINE constexpr explicit TTupleImpl(FForwardingConstructor, ArgTypes&&... Args)
|
||||
: TTupleBasicElement<Ts, Indices>(Forward<ArgTypes>(Args))...
|
||||
template <typename... Us>
|
||||
FORCEINLINE constexpr explicit TTupleImpl(FForwardingConstructor, Us&&... Args)
|
||||
: TTupleBasicElement<Ts, Indices>(Forward<Us>(Args))...
|
||||
{ }
|
||||
|
||||
template <typename TupleType>
|
||||
@ -301,14 +301,17 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor. Value-initializes all elements, if any. */
|
||||
FORCEINLINE constexpr TTuple() = default;
|
||||
|
||||
template <typename... ArgTypes> requires (sizeof...(Ts) >= 1 && sizeof...(ArgTypes) == sizeof...(Ts))
|
||||
&& (true && ... && CConstructibleFrom<Ts, ArgTypes&&>)
|
||||
FORCEINLINE constexpr explicit (!(true && ... && CConvertibleTo<ArgTypes&&, Ts>)) TTuple(ArgTypes&&... Args)
|
||||
: Super(NAMESPACE_PRIVATE::ForwardingConstructor, Forward<ArgTypes>(Args)...)
|
||||
/** Converting constructor. Initializes each element of the tuple with the corresponding value in Forward<Us>(Args). */
|
||||
template <typename... Us> requires (sizeof...(Ts) >= 1 && sizeof...(Us) == sizeof...(Ts))
|
||||
&& (true && ... && CConstructibleFrom<Ts, Us&&>)
|
||||
FORCEINLINE constexpr explicit (!(true && ... && CConvertibleTo<Us&&, Ts>)) TTuple(Us&&... Args)
|
||||
: Super(NAMESPACE_PRIVATE::ForwardingConstructor, Forward<Us>(Args)...)
|
||||
{ }
|
||||
|
||||
/** Converting copy constructor. Initializes each element of the tuple with the corresponding element of other. */
|
||||
template <typename... Us> requires (sizeof...(Us) == sizeof...(Ts)
|
||||
&& (true && ... && CConstructibleFrom<Ts, const Us&>)
|
||||
&& NAMESPACE_PRIVATE::TTupleConvertCopy<sizeof...(Ts) != 1, Ts..., Us...>::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 <typename... Us> requires (sizeof...(Us) == sizeof...(Ts)
|
||||
&& (true && ... && CConstructibleFrom<Ts, Us&&>)
|
||||
&& NAMESPACE_PRIVATE::TTupleConvertMove<sizeof...(Ts) != 1, Ts..., Us...>::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 <typename... Us> requires (sizeof...(Us) == sizeof...(Ts)
|
||||
&& (true && ... && CAssignableFrom<Ts&, const Us&>))
|
||||
FORCEINLINE constexpr TTuple& operator=(const TTuple<Us...>& InValue)
|
||||
@ -334,6 +340,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Converting move assignment operator. Assigns each element of other to the corresponding element of this. */
|
||||
template <typename... Us> requires (sizeof...(Us) == sizeof...(Ts)
|
||||
&& (true && ... && CAssignableFrom<Ts&, Us&&>))
|
||||
FORCEINLINE constexpr TTuple& operator=(TTuple<Us...>&& 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 <typename... Us> requires (sizeof...(Ts) == sizeof...(Us) && NAMESPACE_PRIVATE::CTTupleWeaklyEqualityComparable<TTypeSequence<Ts...>, TTypeSequence<Us...>>)
|
||||
friend FORCEINLINE constexpr bool operator==(const TTuple& LHS, const TTuple<Us...>& RHS)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TTuple& LHS, const TTuple<Us...>& RHS)
|
||||
{
|
||||
if constexpr (sizeof...(Ts) != sizeof...(Us)) return false;
|
||||
|
||||
@ -357,31 +366,35 @@ public:
|
||||
(TMakeIndexSequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
/** Compares lhs and rhs lexicographically by synthesized three-way comparison. */
|
||||
template <typename... Us> requires (sizeof...(Ts) == sizeof...(Us) && NAMESPACE_PRIVATE::CTTupleSynthThreeWayComparable<TTypeSequence<Ts...>, TTypeSequence<Us...>>)
|
||||
friend FORCEINLINE constexpr TCommonComparisonCategory<TSynthThreeWayResult<Ts, Us>...> operator<=>(const TTuple& LHS, const TTuple<Us...>& RHS)
|
||||
NODISCARD friend FORCEINLINE constexpr TCommonComparisonCategory<TSynthThreeWayResult<Ts, Us>...> operator<=>(const TTuple& LHS, const TTuple<Us...>& RHS)
|
||||
{
|
||||
using R = TCommonComparisonCategory<TSynthThreeWayResult<Ts, Us>...>;
|
||||
return NAMESPACE_PRIVATE::TTupleThreeWay<R, TMakeIndexSequence<sizeof...(Ts)>>::Do(LHS, RHS);
|
||||
}
|
||||
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast<const NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast<const volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast<const NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast<const volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
/** Extracts the Ith element from the tuple. I must be an integer value in [0, sizeof...(Ts)). */
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast<const NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast<const volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>& >(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast<const NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast<const volatile NAMESPACE_PRIVATE::TTupleBasicElement<TTupleElement<I, TTuple<Ts...>>, I>&&>(*this).GetValue(); }
|
||||
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast<const TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast<const volatile TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast<const TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast<const volatile TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
/** Extracts the element of the tuple whose type is T. Fails to compile unless the tuple has exactly one element of that type. */
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { return static_cast< TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const & { return static_cast<const TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile& { return static_cast< volatile TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile& { return static_cast<const volatile TTuple& >(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { return static_cast< TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const && { return static_cast<const TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() volatile&& { return static_cast< volatile TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast<const volatile TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple<Ts...>>>(); }
|
||||
|
||||
/** Invoke the Callable object 'Func' with a tuple of arguments. */
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) & { return Helper::Apply(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const & { return Helper::Apply(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile& { return Helper::Apply(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
@ -391,38 +404,42 @@ public:
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile&& { return Helper::Apply(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile&& { return Helper::Apply(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) & { return Helper::Transform(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const & { return Helper::Transform(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile& { return Helper::Transform(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile& { return Helper::Transform(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) && { return Helper::Transform(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const && { return Helper::Transform(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile&& { return Helper::Transform(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile&& { return Helper::Transform(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
/** Transform a tuple into another tuple using the given function. */
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) & { return Helper::Transform(Forward<F>(Func), static_cast< TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const & { return Helper::Transform(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile& { return Helper::Transform(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile& { return Helper::Transform(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) && { return Helper::Transform(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const && { return Helper::Transform(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile&& { return Helper::Transform(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename F> requires (true && ... && (CInvocable<F, Ts> && !CSameAs<void, TInvokeResult<F, Ts>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile&& { return Helper::Transform(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
|
||||
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() & { return Helper::template Construct<T>(static_cast< TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() const & { return Helper::template Construct<T>(static_cast<const TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() volatile& { return Helper::template Construct<T>(static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() const volatile& { return Helper::template Construct<T>(static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() && { return Helper::template Construct<T>(static_cast< TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() const && { return Helper::template Construct<T>(static_cast<const TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() volatile&& { return Helper::template Construct<T>(static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) FORCEINLINE constexpr T Construct() const volatile&& { return Helper::template Construct<T>(static_cast<const volatile TTuple&&>(*this)); }
|
||||
/** Constructs an object of type T with a tuple as an argument. */
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() & { return Helper::template Construct<T>(static_cast< TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const & { return Helper::template Construct<T>(static_cast<const TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile& { return Helper::template Construct<T>(static_cast< volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile& { return Helper::template Construct<T>(static_cast<const volatile TTuple& >(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() && { return Helper::template Construct<T>(static_cast< TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const && { return Helper::template Construct<T>(static_cast<const TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile&& { return Helper::template Construct<T>(static_cast< volatile TTuple&&>(*this)); }
|
||||
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile&& { return Helper::template Construct<T>(static_cast<const volatile TTuple&&>(*this)); }
|
||||
|
||||
FORCEINLINE constexpr size_t GetTypeHash() const requires (true && ... && CHashable<Ts>)
|
||||
/** Overloads the GetTypeHash algorithm for TTuple. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TTuple& A) requires (true && ... && CHashable<Ts>)
|
||||
{
|
||||
return [this]<size_t... Indices>(TIndexSequence<Indices...>) -> size_t
|
||||
return [&A]<size_t... Indices>(TIndexSequence<Indices...>) -> size_t
|
||||
{
|
||||
return HashCombine(NAMESPACE_REDCRAFT::GetTypeHash(GetValue<Indices>())...);
|
||||
return HashCombine(GetTypeHash(A.template GetValue<Indices>())...);
|
||||
}
|
||||
(TMakeIndexSequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr void Swap(TTuple& InValue) requires (true && ... && (CMoveConstructible<Ts> && CSwappable<Ts>))
|
||||
/** Overloads the Swap algorithm for TTuple. */
|
||||
friend FORCEINLINE constexpr void Swap(TTuple& A, TTuple& B) requires (true && ... && (CMoveConstructible<Ts> && CSwappable<Ts>))
|
||||
{
|
||||
[&A = *this, &B = InValue]<size_t... Indices>(TIndexSequence<Indices...>)
|
||||
[&A, &B]<size_t... Indices>(TIndexSequence<Indices...>)
|
||||
{
|
||||
((NAMESPACE_REDCRAFT::Swap(A.template GetValue<Indices>(), B.template GetValue<Indices>())), ...);
|
||||
((Swap(A.template GetValue<Indices>(), B.template GetValue<Indices>())), ...);
|
||||
}
|
||||
(TMakeIndexSequence<sizeof...(Ts)>());
|
||||
}
|
||||
@ -435,18 +452,31 @@ TTuple(Ts...) -> TTuple<Ts...>;
|
||||
template <typename T, typename U>
|
||||
using TPair = TTuple<T, U>;
|
||||
|
||||
/** Creates a tuple object of the type defined by the argument types. */
|
||||
template <typename... Ts>
|
||||
FORCEINLINE constexpr TTuple<TUnwrapRefDecay<Ts>...> MakeTuple(Ts&&... Args)
|
||||
{
|
||||
return TTuple<TUnwrapRefDecay<Ts>...>(Forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tuple of lvalue references or unpacks a tuple into individual objects.
|
||||
*
|
||||
* TTuple<FString, float, TArray<int32>> SomeFunction();
|
||||
*
|
||||
* FString Ret1;
|
||||
* float Ret2;
|
||||
* TArray<int32> Ret3;
|
||||
*
|
||||
* Tie(Ret1, Ret2, Ret3) = SomeFunction();
|
||||
*/
|
||||
template <typename... Ts>
|
||||
FORCEINLINE constexpr TTuple<Ts&...> Tie(Ts&... Args)
|
||||
{
|
||||
return TTuple<Ts&...>(Args...);
|
||||
}
|
||||
|
||||
/** Creates a tuple of forwarding references. */
|
||||
template <typename... Ts>
|
||||
FORCEINLINE constexpr TTuple<Ts&&...> ForwardAsTuple(Ts&&... Args)
|
||||
{
|
||||
@ -552,6 +582,7 @@ NAMESPACE_PRIVATE_END
|
||||
template <typename... TTupleTypes> requires (true && ... && CTTuple<TRemoveCVRef<TTupleTypes>>)
|
||||
using TTupleCatResult = typename NAMESPACE_PRIVATE::TTupleCatResultImpl<TRemoveReference<TTupleTypes>..., NAMESPACE_PRIVATE::FTupleEndFlag>::Type;;
|
||||
|
||||
/** Creates a tuple by concatenating any number of tuples. */
|
||||
template <typename... TTupleTypes> requires (true && ... && CTTuple<TRemoveCVRef<TTupleTypes>>)
|
||||
FORCEINLINE constexpr decltype(auto) TupleCat(TTupleTypes&&... Args)
|
||||
{
|
||||
@ -560,6 +591,21 @@ FORCEINLINE constexpr decltype(auto) TupleCat(TTupleTypes&&... Args)
|
||||
else return NAMESPACE_PRIVATE::TTupleCatImpl<R>::Do(Forward<TTupleTypes>(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<int32, float64, FString>& TupleA, const TTuple<bool, float32, FString>& 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 <typename F, typename FirstTupleType, typename... TupleTypes>
|
||||
requires (CTTuple<TRemoveReference<FirstTupleType>> && (true && ... && CTTuple<TRemoveReference<TupleTypes>>))
|
||||
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 <typename... Ts> struct tuple_size<NAMESPACE_REDCRAFT::TTuple<Ts...>> : integral_constant<size_t, NAMESPACE_REDCRAFT::TTupleArity<NAMESPACE_REDCRAFT::TTuple<Ts...>>> { };
|
||||
template <size_t I, typename... Ts> struct tuple_element<I, NAMESPACE_REDCRAFT::TTuple<Ts...>> { using type = NAMESPACE_REDCRAFT::TTupleElement<I, NAMESPACE_REDCRAFT::TTuple<Ts...>>; };
|
||||
|
||||
@ -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 <size_t Index, typename ...Ts> FORCEINLINE constexpr decltype(auto) get( TTuple<Ts...>& InValue) { return static_cast< TTuple<Ts...>& >(InValue).template GetValue<Index>(); }
|
||||
template <size_t Index, typename ...Ts> FORCEINLINE constexpr decltype(auto) get(const TTuple<Ts...>& InValue) { return static_cast<const TTuple<Ts...>& >(InValue).template GetValue<Index>(); }
|
||||
template <size_t Index, typename ...Ts> FORCEINLINE constexpr decltype(auto) get( volatile TTuple<Ts...>& InValue) { return static_cast< volatile TTuple<Ts...>& >(InValue).template GetValue<Index>(); }
|
||||
|
@ -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 <typename... Ts> requires (true && ... && CConvertibleTo<Ts, size_t>)
|
||||
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 <CIntegral T>
|
||||
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 <CFloatingPoint T>
|
||||
FORCEINLINE constexpr size_t GetTypeHash(T A)
|
||||
{
|
||||
@ -74,24 +80,21 @@ FORCEINLINE constexpr size_t GetTypeHash(T A)
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for CEnum. */
|
||||
template <CEnum T>
|
||||
FORCEINLINE constexpr size_t GetTypeHash(T A)
|
||||
{
|
||||
return GetTypeHash(static_cast<TUnderlyingType<T>>(A));
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for CPointer. */
|
||||
template <typename T> requires (CPointer<T> || CSameAs<T, nullptr_t>)
|
||||
FORCEINLINE constexpr size_t GetTypeHash(T A)
|
||||
{
|
||||
return GetTypeHash(reinterpret_cast<intptr>(A));
|
||||
}
|
||||
|
||||
template <typename T> requires (requires(const T& A) { { GetTypeHash(A.GetTypeHash()) } -> CSameAs<size_t>; })
|
||||
FORCEINLINE constexpr size_t GetTypeHash(const T& A)
|
||||
{
|
||||
return GetTypeHash(A.GetTypeHash());
|
||||
}
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for T::hash_code(). */
|
||||
template <typename T> requires (requires(const T& A) { { GetTypeHash(A.hash_code()) } -> CSameAs<size_t>; })
|
||||
FORCEINLINE constexpr size_t GetTypeHash(const T& A)
|
||||
{
|
||||
|
@ -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 <typename T>
|
||||
FORCEINLINE constexpr const T& AsConst(T& Ref)
|
||||
{
|
||||
return Ref;
|
||||
}
|
||||
|
||||
/** The const rvalue reference overload is deleted to disallow rvalue arguments. */
|
||||
template <typename T>
|
||||
void AsConst(const T&& Ref) = delete;
|
||||
|
||||
/** MoveTemp will cast a reference to an rvalue reference. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr TRemoveReference<T>&& MoveTemp(T&& Obj)
|
||||
{
|
||||
@ -25,52 +28,52 @@ FORCEINLINE constexpr TRemoveReference<T>&& MoveTemp(T&& Obj)
|
||||
return static_cast<CastType&&>(Obj);
|
||||
}
|
||||
|
||||
/** CopyTemp will enforce the creation of an rvalue which can bind to rvalue reference parameters. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr T CopyTemp(T& Obj)
|
||||
{
|
||||
return const_cast<const T&>(Obj);
|
||||
}
|
||||
|
||||
/** CopyTemp will enforce the creation of an rvalue which can bind to rvalue reference parameters. */
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
FORCEINLINE constexpr T&& Forward(TRemoveReference<T>& Obj)
|
||||
{
|
||||
return static_cast<T&&>(Obj);
|
||||
}
|
||||
|
||||
/** Forwards lvalues as either lvalues or as rvalues, depending on T. */
|
||||
template <typename T>
|
||||
FORCEINLINE constexpr T&& Forward(TRemoveReference<T>&& Obj)
|
||||
{
|
||||
return static_cast<T&&>(Obj);
|
||||
}
|
||||
|
||||
template <typename T> requires (requires(T& A, T& B) { A.Swap(B); }
|
||||
|| (CMoveConstructible<T> && CMoveAssignable<T>))
|
||||
/** Exchanges the given values. */
|
||||
template <typename T> requires (CMoveConstructible<T> && CMoveAssignable<T>)
|
||||
FORCEINLINE constexpr void Swap(T& A, T& B)
|
||||
{
|
||||
if constexpr (requires(T& A, T& B) { A.Swap(B); })
|
||||
{
|
||||
A.Swap(B);
|
||||
}
|
||||
else
|
||||
{
|
||||
T Temp = MoveTemp(A);
|
||||
A = MoveTemp(B);
|
||||
B = MoveTemp(Temp);
|
||||
}
|
||||
}
|
||||
|
||||
/** Replaces the value of 'A' with 'B' and returns the old value of 'A'. */
|
||||
template <typename T, typename U = T> requires (CMoveConstructible<T> && CAssignableFrom<T&, U>)
|
||||
FORCEINLINE constexpr T Exchange(T& A, U&& B)
|
||||
{
|
||||
@ -79,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 <typename T>
|
||||
TAddRValueReference<T> DeclVal();
|
||||
|
||||
/** Obtains the actual address of the object or function arg, even in presence of overloaded operator&. */
|
||||
template <typename T> requires (CObject<T>)
|
||||
FORCEINLINE constexpr T* AddressOf(T& Object)
|
||||
{
|
||||
return reinterpret_cast<T*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(Object)));
|
||||
}
|
||||
|
||||
/** Obtains the actual address of the object or function arg, even in presence of overloaded operator&. */
|
||||
template <typename T> requires (!CObject<T>)
|
||||
FORCEINLINE constexpr T* AddressOf(T& Object)
|
||||
{
|
||||
return &Object;
|
||||
}
|
||||
|
||||
/** Rvalue overload is deleted to prevent taking the address of const rvalues. */
|
||||
template <typename T>
|
||||
const T* AddressOf(const T&&) = delete;
|
||||
|
||||
struct FIgnore
|
||||
{
|
||||
template <typename T>
|
||||
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
|
||||
@ -117,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 <typename... Ts>
|
||||
struct TOverloaded : Ts...
|
||||
{
|
||||
|
@ -79,60 +79,83 @@ inline constexpr size_t TVariantIndex = NAMESPACE_PRIVATE::TVariantIndexImpl<T,
|
||||
template <size_t I, CTVariant U>
|
||||
using TVariantAlternative = TCopyCV<U, typename NAMESPACE_PRIVATE::TVariantAlternativeImpl<I, TRemoveCV<U>>::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 <typename... Ts> requires (sizeof...(Ts) > 0 && (true && ... && CDestructible<Ts>))
|
||||
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<Ts>) = default;
|
||||
|
||||
/** Copies content of other into a new instance. */
|
||||
FORCEINLINE constexpr TVariant(const TVariant& InValue) requires ((true && ... && CCopyConstructible<Ts>) && !(true && ... && CTriviallyCopyConstructible<Ts>))
|
||||
: TypeIndex(static_cast<uint8>(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<Ts>) = default;
|
||||
|
||||
/** Moves content of other into a new instance. */
|
||||
FORCEINLINE constexpr TVariant(TVariant&& InValue) requires ((true && ... && CMoveConstructible<Ts>) && !(true && ... && CTriviallyMoveConstructible<Ts>))
|
||||
: TypeIndex(static_cast<uint8>(InValue.GetIndex()))
|
||||
{
|
||||
if (IsValid()) MoveConstructImpl[InValue.GetIndex()](&Value, &InValue.Value);
|
||||
}
|
||||
|
||||
template <size_t I, typename... ArgTypes> requires (I < sizeof...(Ts)
|
||||
&& CConstructibleFrom<TVariantAlternative<I, TVariant<Ts...>>, ArgTypes...>)
|
||||
FORCEINLINE constexpr explicit TVariant(TInPlaceIndex<I>, ArgTypes&&... Args)
|
||||
: TypeIndex(I)
|
||||
{
|
||||
using SelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
new (&Value) SelectedType(Forward<ArgTypes>(Args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... ArgTypes> requires (CConstructibleFrom<T, ArgTypes...>)
|
||||
FORCEINLINE constexpr explicit TVariant(TInPlaceType<T>, ArgTypes&&... Args)
|
||||
: TVariant(InPlaceIndex<TVariantIndex<T, TVariant<Ts...>>>, Forward<ArgTypes>(Args)...)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Converting constructor. Constructs a variant holding the alternative type that would be selected
|
||||
* by overload resolution for the expression F(Forward<T>(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<T>(InValue) }; is valid for some invented variable x.
|
||||
* Direct-initializes the contained value as if by direct non-list-initialization from Forward<T>(InValue).
|
||||
*/
|
||||
template <typename T> requires (requires { typename NAMESPACE_PRIVATE::TVariantSelectedType<T, Ts...>; }
|
||||
&& !CTInPlaceType<TRemoveCVRef<T>> && !CTInPlaceIndex<TRemoveCVRef<T>>
|
||||
&& !CBaseOf<TVariant, TRemoveCVRef<T>>)
|
||||
&& !CSameAs<TVariant, TRemoveCVRef<T>>)
|
||||
FORCEINLINE constexpr TVariant(T&& InValue) : TVariant(InPlaceType<NAMESPACE_PRIVATE::TVariantSelectedType<T, Ts...>>, Forward<T>(InValue))
|
||||
{ }
|
||||
|
||||
/** Constructs a variant with the specified alternative T and initializes the contained value with the arguments Forward<Us>(Args).... */
|
||||
template <typename T, typename... Us> requires (CConstructibleFrom<T, Us...>)
|
||||
FORCEINLINE constexpr explicit TVariant(TInPlaceType<T>, Us&&... Args)
|
||||
: TVariant(InPlaceIndex<TVariantIndex<T, TVariant<Ts...>>>, Forward<Us>(Args)...)
|
||||
{ }
|
||||
|
||||
/** Constructs a variant with the alternative T specified by the index I and initializes the contained value with the arguments Forward<Us>(Args).... */
|
||||
template <size_t I, typename... Us> requires (I < sizeof...(Ts)
|
||||
&& CConstructibleFrom<TVariantAlternative<I, TVariant<Ts...>>, Us...>)
|
||||
FORCEINLINE constexpr explicit TVariant(TInPlaceIndex<I>, Us&&... Args)
|
||||
: TypeIndex(I)
|
||||
{
|
||||
using SelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
new (&Value) SelectedType(Forward<Us>(Args)...);
|
||||
}
|
||||
|
||||
/** Destroys the contained object, if any, as if by a call to Reset(). */
|
||||
FORCEINLINE constexpr ~TVariant() requires (true && ... && CTriviallyDestructible<Ts>) = default;
|
||||
|
||||
/** Destroys the contained object, if any, as if by a call to Reset(). */
|
||||
FORCEINLINE constexpr ~TVariant() requires (!(true && ... && CTriviallyDestructible<Ts>))
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
/** Assigns by copying the state of 'InValue'. */
|
||||
FORCEINLINE constexpr TVariant& operator=(const TVariant& InValue) requires (true && ... && (CTriviallyCopyConstructible<Ts> && CTriviallyCopyAssignable<Ts>)) = default;
|
||||
|
||||
/** Assigns by copying the state of 'InValue'. */
|
||||
constexpr TVariant& operator=(const TVariant& InValue) requires ((true && ... && (CCopyConstructible<Ts> && CCopyAssignable<Ts>))
|
||||
&& !(true && ... && (CTriviallyCopyConstructible<Ts> && CTriviallyCopyAssignable<Ts>)))
|
||||
{
|
||||
@ -155,8 +178,10 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assigns by moving the state of 'InValue'. */
|
||||
FORCEINLINE constexpr TVariant& operator=(TVariant&& InValue) requires (true && ... && (CTriviallyMoveConstructible<Ts> && CTriviallyMoveAssignable<Ts>)) = default;
|
||||
|
||||
/** Assigns by moving the state of 'InValue'. */
|
||||
constexpr TVariant& operator=(TVariant&& InValue) requires ((true && ... && (CMoveConstructible<Ts> && CMoveAssignable<Ts>))
|
||||
&& !(true && ... && (CTriviallyMoveConstructible<Ts> && CTriviallyMoveAssignable<Ts>)))
|
||||
{
|
||||
@ -179,6 +204,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Converting assignment. Constructs a variant holding the alternative type that would be selected by overload resolution. */
|
||||
template <typename T> requires (requires { typename NAMESPACE_PRIVATE::TVariantSelectedType<T, Ts...>; })
|
||||
FORCEINLINE constexpr TVariant& operator=(T&& InValue)
|
||||
{
|
||||
@ -195,7 +221,8 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const TVariant& LHS, const TVariant& RHS) requires (true && ... && CEqualityComparable<Ts>)
|
||||
/** Check if the two variants are equivalent. */
|
||||
NODISCARD friend constexpr bool operator==(const TVariant& LHS, const TVariant& RHS) requires (true && ... && CEqualityComparable<Ts>)
|
||||
{
|
||||
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<Ts>)
|
||||
/** Check the order relationship between two variants. */
|
||||
NODISCARD friend constexpr partial_ordering operator<=>(const TVariant& LHS, const TVariant& RHS) requires (true && ... && CSynthThreeWayComparable<Ts>)
|
||||
{
|
||||
if (LHS.GetIndex() != RHS.GetIndex()) return partial_ordering::unordered;
|
||||
if (LHS.IsValid() == false) return partial_ordering::equivalent;
|
||||
@ -217,64 +245,86 @@ public:
|
||||
return CompareImpl[LHS.GetIndex()](&LHS.Value, &RHS.Value);
|
||||
}
|
||||
|
||||
template <typename T> requires (!CBaseOf<TVariant, T> && CEqualityComparable<T>)
|
||||
FORCEINLINE constexpr bool operator==(const T& InValue) const&
|
||||
/** Check if the variant value is equivalent to 'InValue'. */
|
||||
template <typename T> requires (!CSameAs<TVariant, T> && CEqualityComparable<T>)
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(const T& InValue) const&
|
||||
{
|
||||
return HoldsAlternative<T>() ? GetValue<T>() == InValue : false;
|
||||
}
|
||||
|
||||
template <typename T> requires (!CBaseOf<TVariant, T> && CEqualityComparable<T>)
|
||||
FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const&
|
||||
/** Check that the variant value is in ordered relationship with 'InValue'. */
|
||||
template <typename T> requires (!CSameAs<TVariant, T> && CEqualityComparable<T>)
|
||||
NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const&
|
||||
{
|
||||
return HoldsAlternative<T>() ? SynthThreeWayCompare(GetValue<T>(), 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(); }
|
||||
|
||||
template <size_t I, typename... ArgTypes> requires (I < sizeof...(Ts)
|
||||
&& CConstructibleFrom<TVariantAlternative<I, TVariant<Ts...>>, ArgTypes...>)
|
||||
FORCEINLINE constexpr TVariantAlternative<I, TVariant<Ts...>>& Emplace(ArgTypes&&... Args)
|
||||
/** Equivalent to Emplace<I>(Forward<Us>(Args)...), where I is the zero-based index of T in Types.... */
|
||||
template <typename T, typename... Us> requires (CConstructibleFrom<T, Us...>)
|
||||
FORCEINLINE constexpr T& Emplace(Us&&... Args)
|
||||
{
|
||||
return Emplace<TVariantIndex<T, TVariant<Ts...>>>(Forward<Us>(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<Us>(Args)....
|
||||
*
|
||||
* @param Args - The arguments to be passed to the constructor of the contained object.
|
||||
*
|
||||
* @return A reference to the new contained object.
|
||||
*/
|
||||
template <size_t I, typename... Us> requires (I < sizeof...(Ts)
|
||||
&& CConstructibleFrom<TVariantAlternative<I, TVariant<Ts...>>, Us...>)
|
||||
FORCEINLINE constexpr TVariantAlternative<I, TVariant<Ts...>>& Emplace(Us&&... Args)
|
||||
{
|
||||
Reset();
|
||||
|
||||
using SelectedType = TVariantAlternative<I, TVariant<Ts...>>;
|
||||
SelectedType* Result = new (&Value) SelectedType(Forward<ArgTypes>(Args)...);
|
||||
SelectedType* Result = new (&Value) SelectedType(Forward<Us>(Args)...);
|
||||
TypeIndex = I;
|
||||
|
||||
return *Result;
|
||||
}
|
||||
|
||||
template <typename T, typename... ArgTypes> requires (CConstructibleFrom<T, ArgTypes...>)
|
||||
FORCEINLINE constexpr T& Emplace(ArgTypes&&... Args)
|
||||
{
|
||||
return Emplace<TVariantIndex<T, TVariant<Ts...>>>(Forward<ArgTypes>(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 <size_t I> FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == I : false; }
|
||||
template <typename T> FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == TVariantIndex<T, TVariant<Ts...>> : false; }
|
||||
/** @return true if the variant currently holds the alternative, false otherwise. */
|
||||
template <size_t I> NODISCARD FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == I : false; }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == TVariantIndex<T, TVariant<Ts...>> : false; }
|
||||
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative<I>(), 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<I, TVariant<Ts...>>*>(&Value); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative<I>(), 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<I, TVariant<Ts...>>*>(&Value)); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative<I>(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast<const TVariantAlternative<I, TVariant<Ts...>>*>(&Value); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative<I>(), 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<const TVariantAlternative<I, TVariant<Ts...>>*>(&Value)); }
|
||||
/** @return The contained object. */
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative<I>(), 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<I, TVariant<Ts...>>*>(&Value); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative<I>(), 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<I, TVariant<Ts...>>*>(&Value)); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative<I>(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast<const TVariantAlternative<I, TVariant<Ts...>>*>(&Value); }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative<I>(), 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<const TVariantAlternative<I, TVariant<Ts...>>*>(&Value)); }
|
||||
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative<T>(), 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 <typename T> FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative<T>(), 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 <typename T> FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative<T>(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast<const T*>(&Value); }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative<T>(), 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<const T*>(&Value)); }
|
||||
/** @return The contained object. */
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() & { checkf(HoldsAlternative<T>(), 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 <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() && { checkf(HoldsAlternative<T>(), 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 <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const& { checkf(HoldsAlternative<T>(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast<const T*>(&Value); }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const&& { checkf(HoldsAlternative<T>(), 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<const T*>(&Value)); }
|
||||
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) Get( TVariantAlternative<I, TVariant<Ts...>>& DefaultValue) & { return HoldsAlternative<I>() ? GetValue<I>() : DefaultValue; }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) FORCEINLINE constexpr decltype(auto) Get(const TVariantAlternative<I, TVariant<Ts...>>& DefaultValue) const& { return HoldsAlternative<I>() ? GetValue<I>() : DefaultValue; }
|
||||
/** @return The contained object when HoldsAlternative<I>() returns true, 'DefaultValue' otherwise. */
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) Get( TVariantAlternative<I, TVariant<Ts...>>& DefaultValue) & { return HoldsAlternative<I>() ? GetValue<I>() : DefaultValue; }
|
||||
template <size_t I> requires (I < sizeof...(Ts)) NODISCARD FORCEINLINE constexpr decltype(auto) Get(const TVariantAlternative<I, TVariant<Ts...>>& DefaultValue) const& { return HoldsAlternative<I>() ? GetValue<I>() : DefaultValue; }
|
||||
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) Get( T& DefaultValue) & { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
template <typename T> FORCEINLINE constexpr decltype(auto) Get(const T& DefaultValue) const& { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
/** @return The contained object when HoldsAlternative<T>() returns true, 'DefaultValue' otherwise. */
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) Get( T& DefaultValue) & { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) Get(const T& DefaultValue) const& { return HoldsAlternative<T>() ? GetValue<T>() : DefaultValue; }
|
||||
|
||||
/** If not empty, destroys the contained object. */
|
||||
FORCEINLINE constexpr void Reset()
|
||||
{
|
||||
if (GetIndex() == INDEX_NONE) return;
|
||||
@ -287,51 +337,45 @@ public:
|
||||
TypeIndex = static_cast<uint8>(INDEX_NONE);
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr size_t GetTypeHash() const requires (true && ... && CHashable<Ts>)
|
||||
/** Overloads the GetTypeHash algorithm for TVariant. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TVariant& A) requires (true && ... && CHashable<Ts>)
|
||||
{
|
||||
if (!IsValid()) return 114514;
|
||||
|
||||
using NAMESPACE_REDCRAFT::GetTypeHash;
|
||||
if (!A.IsValid()) return 114514;
|
||||
|
||||
using FHashImpl = size_t(*)(const void*);
|
||||
constexpr FHashImpl HashImpl[] = { [](const void* This) -> size_t { return GetTypeHash(*reinterpret_cast<const Ts*>(This)); }... };
|
||||
|
||||
return HashCombine(GetTypeHash(GetIndex()), HashImpl[GetIndex()](&Value));
|
||||
return HashCombine(GetTypeHash(A.GetIndex()), HashImpl[A.GetIndex()](&A.Value));
|
||||
}
|
||||
|
||||
constexpr void Swap(TVariant& InValue) requires (true && ... && (CMoveConstructible<Ts> && CSwappable<Ts>))
|
||||
/** Overloads the Swap algorithm for TVariant. */
|
||||
friend constexpr void Swap(TVariant& A, TVariant& B) requires (true && ... && (CMoveConstructible<Ts> && CSwappable<Ts>))
|
||||
{
|
||||
if (!IsValid() && !InValue.IsValid()) return;
|
||||
if (!A.IsValid() && !B.IsValid()) return;
|
||||
|
||||
if (IsValid() && !InValue.IsValid())
|
||||
if (A.IsValid() && !B.IsValid())
|
||||
{
|
||||
InValue = MoveTemp(*this);
|
||||
Reset();
|
||||
return;
|
||||
B = MoveTemp(A);
|
||||
A.Reset();
|
||||
}
|
||||
|
||||
if (InValue.IsValid() && !IsValid())
|
||||
else if (!A.IsValid() && B.IsValid())
|
||||
{
|
||||
*this = MoveTemp(InValue);
|
||||
InValue.Reset();
|
||||
return;
|
||||
A = MoveTemp(B);
|
||||
B.Reset();
|
||||
}
|
||||
|
||||
if (GetIndex() == InValue.GetIndex())
|
||||
else if (A.GetIndex() == B.GetIndex())
|
||||
{
|
||||
using NAMESPACE_REDCRAFT::Swap;
|
||||
|
||||
using FSwapImpl = void(*)(void*, void*);
|
||||
constexpr FSwapImpl SwapImpl[] = { [](void* A, void* B) { Swap(*reinterpret_cast<Ts*>(A), *reinterpret_cast<Ts*>(B)); }... };
|
||||
|
||||
SwapImpl[GetIndex()](&Value, &InValue.Value);
|
||||
|
||||
return;
|
||||
SwapImpl[A.GetIndex()](&A.Value, &B.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
TVariant Temp = MoveTemp(A);
|
||||
A = MoveTemp(B);
|
||||
B = MoveTemp(Temp);
|
||||
}
|
||||
|
||||
TVariant Temp = MoveTemp(*this);
|
||||
*this = MoveTemp(InValue);
|
||||
InValue = MoveTemp(Temp);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -485,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 <typename F, typename FirstVariantType, typename... VariantTypes>
|
||||
requires (CTVariant<TRemoveReference<FirstVariantType>> && (true && ... && CTVariant<TRemoveReference<VariantTypes>>))
|
||||
constexpr decltype(auto) Visit(F&& Func, FirstVariantType&& FirstVariant, VariantTypes&&... Variants)
|
||||
@ -493,6 +538,7 @@ constexpr decltype(auto) Visit(F&& Func, FirstVariantType&& FirstVariant, Varian
|
||||
return NAMESPACE_PRIVATE::TVariantVisitImpl<F, FirstVariantType, VariantTypes...>::Do(Forward<F>(Func), Forward<FirstVariantType>(FirstVariant), Forward<VariantTypes>(Variants)...);
|
||||
}
|
||||
|
||||
/** Applies the visitor 'Func' (Callable that can be called with any combination of types from variants) to the variants 'Variants'. */
|
||||
template <typename Ret, typename F, typename FirstVariantType, typename... VariantTypes>
|
||||
requires (CTVariant<TRemoveReference<FirstVariantType>> && (true && ... && CTVariant<TRemoveReference<VariantTypes>>))
|
||||
constexpr Ret Visit(F&& Func, FirstVariantType&& FirstVariant, VariantTypes&&... Variants)
|
||||
|
Loading…
Reference in New Issue
Block a user