Compare commits

...

2 Commits

15 changed files with 992 additions and 394 deletions

View File

@ -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>();
}
{

View File

@ -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)
{

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "CoreTypes.h"
#include "TypeTraits/TypeTraits.h"
@ -13,100 +13,280 @@ NAMESPACE_MODULE_BEGIN(Utility)
NAMESPACE_BEGIN(Memory)
/**
* Default allocator alignment.
* Blocks >= 16 bytes will be 16-byte-aligned, Blocks < 16 will be 8-byte aligned. If the allocator does
* not support allocation alignment, the alignment will be ignored.
*/
inline constexpr size_t DefaultAlignment = 0;
/**
* Minimum allocator alignment.
*/
inline constexpr size_t MinimumAlignment = 8;
#ifdef __cpp_lib_hardware_interference_size
inline constexpr size_t DestructiveInterference = std::hardware_destructive_interference_size;
/**
* Minimum offset between two objects to avoid false sharing.
*
* struct FTwoCacheLiner { // occupies two cache lines
* alignas(DestructiveInterference) TAtomic<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)
@ -117,8 +297,8 @@ NAMESPACE_REDCRAFT_END
#pragma warning(disable : 28251)
// The global overload operators new/delete do not cross .dll boundaries, and the macros should be placed in the .cpp of each module.
#define REPLACEMENT_OPERATOR_NEW_AND_DELETE \
void* operator new(std::size_t Count) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count); } \
void* operator new(std::size_t Count, std::align_val_t Alignment) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count, static_cast<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); }
#define REPLACEMENT_OPERATOR_NEW_AND_DELETE \
NODISCARD void* operator new(std::size_t Count) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count); } \
NODISCARD void* operator new(std::size_t Count, std::align_val_t Alignment) { return NAMESPACE_REDCRAFT::Memory::Malloc(Count, static_cast<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); }

View File

@ -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)
{

View File

@ -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 (HoldsAlternative<DecayedType>())
if constexpr (CAssignableFrom<DecayedType, T&&>)
{
GetValue<DecayedType>() = Forward<T>(InValue);
}
else
{
Destroy();
EmplaceImpl<DecayedType>(Forward<T>(InValue));
if (HoldsAlternative<DecayedType>())
{
GetValue<DecayedType>() = Forward<T>(InValue);
return *this;
}
}
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();
}
return;
FAny Temp = MoveTemp(A);
A = MoveTemp(B);
B = MoveTemp(Temp);
}
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
{

View File

@ -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;

View File

@ -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) };
}

View File

@ -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)...);

View File

@ -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;

View File

@ -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)...);
}

View File

@ -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:

View File

@ -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>(); }

View File

@ -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)
{

View File

@ -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);
}
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...
{

View File

@ -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)