diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index 56bec7f..30e2532 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -2,6 +2,10 @@ #include "Miscellaneous/AssertionMacros.h" #include "Templates/Templates.h" +#pragma warning(disable : 4930) +#pragma warning(disable : 4101) +#pragma warning(disable : 4244) + NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) @@ -12,6 +16,7 @@ void TestTemplates() TestReferenceWrapper(); TestCompare(); TestOptional(); + TestVariant(); TestMiscellaneous(); } @@ -270,6 +275,172 @@ void TestOptional() TempZ = FTracker(); } +void TestVariant() +{ + TVariant TempA; + TVariant TempB(Invalid); + TVariant TempC(InPlaceType, 0); + TVariant TempD(0); + TVariant TempE(0l); + TVariant TempF(0.0); + TVariant TempG(TempA); + TVariant TempH(TempD); + TVariant TempI(TVariant(0)); + TVariant TempJ(TVariant(Invalid)); + + TVariant TempK, TempL, TempM, TempN; + TempK = TempA; + TempL = TempD; + TempM = TVariant(0); + TempN = TVariant(Invalid); + + TempL = 303; + TempM = 404; + + TVariant TempO; + TempO.Emplace(202); + TempO.Emplace<0>(404); + + always_check(TempO); + always_check(TempO.IsValid()); + + always_check(TempO == 404); + always_check(TempO.GetValue() == 404); + always_check(TempO.Get<0>(500) == 404); + + TempO.Reset(); + always_check(TempO == TempO); + always_check(!(TempO != TempO)); + always_check(TempO.Get(500) == 500); + + int32 TempP = 200; + TempO = TempP; + TempO = 300; + + always_check(TempO != TempA); + always_check(TempO != TempD); + always_check(TempO == TempO); + always_check(TempO == 300); + always_check(300 == TempO); + + Swap(TempD, TempA); + + int16 TempQ = 1024; + TVariant TempR = TempQ; + + TVariant TempS(InPlaceType, TempQ); + TVariant TempT(TempQ); + TVariant TempU(TempR); + TVariant TempV(TVariant(2048)); + + TVariant TempW, TempX, TempY; + TempW = TempQ; + TempX = TempR; + TempY = TVariant(2048); + + Swap(TempW, TempX); + Swap(TempW, TempX); + + struct FTracker + { + FTracker() { } + FTracker(const FTracker& InValue) { always_check_no_entry(); } + FTracker(FTracker&& InValue) { } + FTracker& operator=(const FTracker& InValue) { always_check_no_entry(); return *this; } + FTracker& operator=(FTracker&& InValue) { return *this; } + }; + + TVariant TempZ(Invalid); + TempZ = TVariant(); + TempZ = FTracker(); + + always_check((TIsSame>::Type>::Value)); + always_check((TIsSame>::Type>::Value)); + always_check((TIsSame>::Type>::Value)); + + always_check((TVariantAlternativeIndex>::Value == 0)); + always_check((TVariantAlternativeIndex>::Value == 1)); + + bool bIsConst; + bool bIsLValue; + bool bIsRValue; + + auto TestQualifiers = [&bIsConst, &bIsLValue, &bIsRValue](auto&& Arg) -> int32 + { + using T = decltype(Arg); + always_check(Arg == 10); + always_check(TIsConst::Type>::Value == bIsConst); + always_check(TIsLValueReference::Value == bIsLValue); + always_check(TIsRValueReference::Value == bIsRValue); + return 0; + }; + + bIsConst = false; + bIsLValue = true; + bIsRValue = false; + + TVariant TempLA = 10; + auto ReturnLA = TempLA.Visit(TestQualifiers); + always_check((TIsSame::Value)); + + bIsConst = true; + bIsLValue = true; + bIsRValue = false; + + const TVariant TempLB = TempLA; + auto ReturnLB = TempLB.Visit(TestQualifiers); + always_check((TIsSame::Value)); + + bIsConst = false; + bIsLValue = false; + bIsRValue = true; + + TVariant TempRA = 10; + auto ReturnRA = MoveTemp(TempRA).Visit(TestQualifiers); + always_check((TIsSame::Value)); + + bIsConst = true; + bIsLValue = false; + bIsRValue = true; + + const TVariant TempRB = TempLA; + auto ReturnRB = MoveTemp(TempRB).Visit(TestQualifiers); + always_check((TIsSame::Value)); + + bIsConst = false; + bIsLValue = true; + bIsRValue = false; + + TVariant TempLC = 10; + auto ReturnLC = TempLC.Visit(TestQualifiers); + always_check((TIsSame::Value)); + + bIsConst = true; + bIsLValue = true; + bIsRValue = false; + + const TVariant TempLD = TempLC; + auto ReturnLD = TempLD.Visit(TestQualifiers); + always_check((TIsSame::Value)); + + bIsConst = false; + bIsLValue = false; + bIsRValue = true; + + TVariant TempRC = 10; + auto ReturnRC = MoveTemp(TempRC).Visit(TestQualifiers); + always_check((TIsSame::Value)); + + bIsConst = true; + bIsLValue = false; + bIsRValue = true; + + const TVariant TempRD = TempLC; + auto ReturnRD = MoveTemp(TempRD).Visit(TestQualifiers); + always_check((TIsSame::Value)); + +} + NAMESPACE_UNNAMED_BEGIN template diff --git a/Redcraft.Utility/Source/Public/Templates/Placeholders.h b/Redcraft.Utility/Source/Public/Templates/Placeholders.h index d1449af..586d638 100644 --- a/Redcraft.Utility/Source/Public/Templates/Placeholders.h +++ b/Redcraft.Utility/Source/Public/Templates/Placeholders.h @@ -1,6 +1,7 @@ #pragma once #include "CoreTypes.h" +#include "TypeTraits/HelperClasses.h" NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) @@ -24,6 +25,12 @@ template inline constexpr TInPlaceType InPlaceType{}; template struct TInPlaceIndex { explicit TInPlaceIndex() = default; }; template inline constexpr TInPlaceIndex InPlaceIndex{}; +template struct TIsInPlaceTypeSpecialization : FFalse { }; +template struct TIsInPlaceTypeSpecialization> : FTrue { }; + +template struct TIsInPlaceIndexSpecialization : FFalse { }; +template struct TIsInPlaceIndexSpecialization> : FTrue { }; + NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Templates/Templates.h b/Redcraft.Utility/Source/Public/Templates/Templates.h index 6b1b495..636b451 100644 --- a/Redcraft.Utility/Source/Public/Templates/Templates.h +++ b/Redcraft.Utility/Source/Public/Templates/Templates.h @@ -9,3 +9,4 @@ #include "Templates/ReferenceWrapper.h" #include "Templates/Compare.h" #include "Templates/Optional.h" +#include "Templates/Variant.h" diff --git a/Redcraft.Utility/Source/Public/Templates/Utility.h b/Redcraft.Utility/Source/Public/Templates/Utility.h index 3019691..767c29a 100644 --- a/Redcraft.Utility/Source/Public/Templates/Utility.h +++ b/Redcraft.Utility/Source/Public/Templates/Utility.h @@ -26,10 +26,6 @@ template constexpr typename TRemoveReference::Type&& MoveTemp(T&& Obj) { typedef typename TRemoveReference::Type CastType; - - static_assert(TIsLValueReference::Value, "MoveTemp called on an rvalue."); - static_assert(!TIsConst::Value, "MoveTemp called on a const object."); - return (CastType&&)Obj; } diff --git a/Redcraft.Utility/Source/Public/Templates/Variant.h b/Redcraft.Utility/Source/Public/Templates/Variant.h new file mode 100644 index 0000000..7fe3c79 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Templates/Variant.h @@ -0,0 +1,526 @@ +#pragma once + +#include "CoreTypes.h" +#include "Templates/Invoke.h" +#include "Templates/Utility.h" +#include "Templates/Placeholders.h" +#include "TypeTraits/TypeTraits.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_PRIVATE_BEGIN + +template +struct TVariantAlternativeIndex; + +template +struct TVariantAlternativeIndex + : TConstant::Value ? 0 : (TVariantAlternativeIndex::Value == static_cast(INDEX_NONE) + ? static_cast(INDEX_NONE) : TVariantAlternativeIndex::Value + 1)> +{ }; + +template +struct TVariantAlternativeIndex : TConstant(INDEX_NONE)> { }; + +template +struct TVariantAlternativeType; + +template +struct TVariantAlternativeType +{ + static_assert(I < sizeof...(Types) + 1, "Variant type index is invalid"); + using Type = TVariantAlternativeType::Type; +}; + +template +struct TVariantAlternativeType<0, T, Types...> { using Type = T; }; + +template +struct TVariantSelectedType; + +template +struct TVariantSelectedType +{ + using TypeAlternativeA = typename TConditional::Value, U, void>::Type; + using TypeAlternativeB = typename TVariantSelectedType::Type; + + using Type = typename TConditional::Value, TypeAlternativeB, + typename TConditional::Value, TypeAlternativeA, + typename TConditional::Value, TypeAlternativeB, TypeAlternativeA>::Type>::Type>::Type; + + // 0 - Type not found + // 1 - Same type found + // 2 - Multiple types found + // 3 - The type found + static constexpr uint8 Flag = TIsSame::Value ? 0 : + TIsSame::Value ? 1 : + !TIsSame::Value && !TIsSame::Value ? 2 : 3; + + static constexpr bool Value = Flag & 1; + +}; + +template +struct TVariantSelectedType +{ + static constexpr uint8 Flag = 0; + using Type = void; +}; + +template +constexpr void VariantDestroy(void* InValue) +{ + if constexpr (!TIsDestructible::Value) check_no_entry(); + else if constexpr (!TIsTriviallyDestructible::Value) + { + typedef T DestructOptionalType; + reinterpret_cast(InValue)->DestructOptionalType::~DestructOptionalType(); + } +} + +using FVariantDestroyFunc = void(*)(void*); + +template +constexpr void VariantCopyConstruct(void* Target, const void* Source) +{ + if constexpr (!TIsCopyConstructible::Value || TIsConst::Value) check_no_entry(); + new(reinterpret_cast(Target)) T(*reinterpret_cast(Source)); +} + +using FVariantCopyConstructFunc = void(*)(void*, const void*); + +template +constexpr void VariantMoveConstruct(void* Target, void* Source) +{ + if constexpr (!TIsMoveConstructible::Value || TIsConst::Value) check_no_entry(); + new(reinterpret_cast(Target)) T(MoveTemp(*reinterpret_cast(Source))); +} + +using FVariantMoveConstructFunc = void(*)(void*, void*); + +template +constexpr void VariantCopyAssign(void* Target, const void* Source) +{ + if constexpr (!TIsCopyAssignable::Value || TIsConst::Value) check_no_entry(); + *reinterpret_cast(Target) = *reinterpret_cast(Source); +} + +using FVariantCopyAssignFunc = void(*)(void*, const void*); + +template +constexpr void VariantMoveAssign(void* Target, void* Source) +{ + if constexpr (!TIsMoveAssignable::Value || TIsConst::Value) check_no_entry(); + *reinterpret_cast(Target) = MoveTemp(*reinterpret_cast(Source)); +} + +using FVariantMoveAssignFunc = void(*)(void*, void*); + +template +constexpr bool VariantEqualityOperator(const void* LHS, const void* RHS) +{ + if constexpr (!CEqualityComparable) check_no_entry(); + else return *reinterpret_cast(LHS) == *reinterpret_cast(RHS); + return false; +} + +using FVariantEqualityOperatorFunc = bool(*)(const void*, const void*); + +template +constexpr bool VariantInequalityOperator(const void* LHS, const void* RHS) +{ + if constexpr (!CEqualityComparable) check_no_entry(); + else return *reinterpret_cast(LHS) != *reinterpret_cast(RHS); + return false; +} + +using FVariantInequalityOperatorFunc = bool(*)(const void*, const void*); + +template +struct TVariantHelper +{ + static constexpr FVariantDestroyFunc DestroyFuncs[] = { VariantDestroy... }; + static constexpr FVariantCopyConstructFunc CopyConstructFuncs[] = { VariantCopyConstruct... }; + static constexpr FVariantMoveConstructFunc MoveConstructFuncs[] = { VariantMoveConstruct... }; + static constexpr FVariantCopyAssignFunc CopyAssignFuncs[] = { VariantCopyAssign... }; + static constexpr FVariantMoveAssignFunc MoveAssignFuncs[] = { VariantMoveAssign... }; + static constexpr FVariantEqualityOperatorFunc EqualityOperatorFuncs[] = { VariantEqualityOperator... }; + static constexpr FVariantInequalityOperatorFunc InequalityOperatorFuncs[] = { VariantInequalityOperator... }; +}; + +template constexpr FVariantDestroyFunc TVariantHelper::DestroyFuncs[]; +template constexpr FVariantCopyConstructFunc TVariantHelper::CopyConstructFuncs[]; +template constexpr FVariantMoveConstructFunc TVariantHelper::MoveConstructFuncs[]; +template constexpr FVariantCopyAssignFunc TVariantHelper::CopyAssignFuncs[]; +template constexpr FVariantMoveAssignFunc TVariantHelper::MoveAssignFuncs[]; +template constexpr FVariantEqualityOperatorFunc TVariantHelper::EqualityOperatorFuncs[]; +template constexpr FVariantInequalityOperatorFunc TVariantHelper::InequalityOperatorFuncs[]; + +template +constexpr R VariantVisitLValue(F&& Func, void* Arg) +{ + if constexpr (!TIsInvocableResult::Value) check_no_entry(); + else if constexpr(TIsVoid::Value) Invoke(Forward(Func), *reinterpret_cast(Arg)); + else return InvokeResult(Forward(Func), *reinterpret_cast(Arg)); +} + +template +using FVariantVisitLValueFunc = R(*)(F&&, void*); + +template +constexpr R VariantVisitRValue(F&& Func, void* Arg) +{ + if constexpr (!TIsInvocableResult::Value) check_no_entry(); + else if constexpr (TIsVoid::Value) Invoke(Forward(Func), MoveTemp(*reinterpret_cast(Arg))); + else return InvokeResult(Forward(Func), MoveTemp(*reinterpret_cast(Arg))); +} + +template +using FVariantVisitRValueFunc = R(*)(F&&, void*); + +template +constexpr R VariantVisitConstLValue(F&& Func, const void* Arg) +{ + if constexpr (!TIsInvocableResult::Value) check_no_entry(); + else if constexpr (TIsVoid::Value) Invoke(Forward(Func), *reinterpret_cast(Arg)); + else return InvokeResult(Forward(Func), *reinterpret_cast(Arg)); +} + +template +using FVariantVisitConstLValueFunc = R(*)(F&&, const void*); + +template +constexpr R VariantVisitConstRValue(F&& Func, const void* Arg) +{ + if constexpr (!TIsInvocableResult::Value) check_no_entry(); + else if constexpr (TIsVoid::Value) Invoke(Forward(Func), MoveTemp(*reinterpret_cast(Arg))); + else return InvokeResult(Forward(Func), MoveTemp(*reinterpret_cast(Arg))); +} + +template +using FVariantVisitConstRValueFunc = R(*)(F&&, const void*); + +template +struct TVariantVisitHelper +{ + static constexpr FVariantVisitLValueFunc VisitLValueFuncs[] = { VariantVisitLValue... }; + static constexpr FVariantVisitRValueFunc VisitRValueFuncs[] = { VariantVisitRValue... }; + static constexpr FVariantVisitConstLValueFunc VisitConstLValueFuncs[] = { VariantVisitConstLValue... }; + static constexpr FVariantVisitConstRValueFunc VisitConstRValueFuncs[] = { VariantVisitConstRValue... }; +}; + +template constexpr FVariantVisitLValueFunc TVariantVisitHelper::VisitLValueFuncs[]; +template constexpr FVariantVisitRValueFunc TVariantVisitHelper::VisitRValueFuncs[]; +template constexpr FVariantVisitConstLValueFunc TVariantVisitHelper::VisitConstLValueFuncs[]; +template constexpr FVariantVisitConstRValueFunc TVariantVisitHelper::VisitConstRValueFuncs[]; + +NAMESPACE_PRIVATE_END + +template +struct TVariant +{ + struct FAlternativeSize : TConstant { }; + + template struct TAlternativeType : NAMESPACE_PRIVATE::TVariantAlternativeType { }; + template struct TAlternativeIndex : NAMESPACE_PRIVATE::TVariantAlternativeIndex { }; + + constexpr TVariant() : TypeIndex(static_cast(INDEX_NONE)) { }; + + constexpr TVariant(FInvalid) : TVariant() { }; + + constexpr TVariant(const TVariant& InValue) + : TypeIndex(InValue.GetIndex()) + { + if (GetIndex() != static_cast(INDEX_NONE)) FHelper::CopyConstructFuncs[InValue.GetIndex()](&Value, &InValue.Value); + } + + constexpr TVariant(TVariant&& InValue) + : TypeIndex(InValue.GetIndex()) + { + if (GetIndex() != static_cast(INDEX_NONE)) FHelper::MoveConstructFuncs[InValue.GetIndex()](&Value, &InValue.Value); + } + + template requires (I < FAlternativeSize::Value) + && TIsConstructible::Type, ArgTypes...>::Value + constexpr explicit TVariant(TInPlaceIndex, ArgTypes&&... Args) + : TypeIndex(I) + { + using SelectedType = typename TAlternativeType::Type; + new(&Value) SelectedType(Forward(Args)...); + } + + template requires (TAlternativeIndex::Value != static_cast(INDEX_NONE)) + && TIsConstructible::Value>::Type, ArgTypes...>::Value + constexpr explicit TVariant(TInPlaceType, ArgTypes&&... Args) + : TVariant(InPlaceIndex::Value>, Forward(Args)...) + { } + + template requires NAMESPACE_PRIVATE::TVariantSelectedType::Type, Types...>::Value + && (!TIsInPlaceTypeSpecialization::Type>::Value) && (!TIsInPlaceIndexSpecialization::Type>::Value) + && (!TIsSame::Type, TVariant>::Value) + constexpr TVariant(T&& InValue) : TVariant(InPlaceType::Type, Types...>::Type>, Forward(InValue)) + { } + + constexpr ~TVariant() + { + Reset(); + } + + constexpr TVariant& operator=(const TVariant& InValue) + { + if (&InValue == this) return *this; + + if (!InValue.IsValid()) + { + Reset(); + return *this; + } + + if (GetIndex() == InValue.GetIndex()) FHelper::CopyAssignFuncs[InValue.GetIndex()](&Value, &InValue.Value); + else + { + Reset(); + FHelper::CopyConstructFuncs[InValue.GetIndex()](&Value, &InValue.Value); + TypeIndex = InValue.GetIndex(); + } + + return *this; + } + + constexpr TVariant& operator=(TVariant&& InValue) + { + if (&InValue == this) return *this; + + if (!InValue.IsValid()) + { + Reset(); + return *this; + } + + if (GetIndex() == InValue.GetIndex()) FHelper::MoveAssignFuncs[InValue.GetIndex()](&Value, &InValue.Value); + else + { + Reset(); + FHelper::MoveConstructFuncs[InValue.GetIndex()](&Value, &InValue.Value); + TypeIndex = InValue.GetIndex(); + } + + return *this; + } + + template requires NAMESPACE_PRIVATE::TVariantSelectedType::Type, Types...>::Value + constexpr TVariant& operator=(T&& InValue) + { + using SelectedType = typename NAMESPACE_PRIVATE::TVariantSelectedType::Type, Types...>::Type; + + if (GetIndex() == TAlternativeIndex::Value) GetValue() = Forward(InValue); + else + { + Reset(); + new(&Value) SelectedType(Forward(InValue)); + TypeIndex = TAlternativeIndex::Value; + } + + return *this; + } + + template requires (I < FAlternativeSize::Value) + && TIsConstructible::Type, ArgTypes...>::Value + constexpr typename TAlternativeType::Type& Emplace(ArgTypes&&... Args) + { + Reset(); + + using SelectedType = typename TAlternativeType::Type; + SelectedType* Result = new(&Value) SelectedType(Forward(Args)...); + TypeIndex = I; + + return *Result; + } + + template requires (TAlternativeIndex::Value != static_cast(INDEX_NONE)) + && TIsConstructible::Value>::Type, ArgTypes...>::Value + constexpr T& Emplace(ArgTypes&&... Args) + { + return Emplace::Value>(Forward(Args)...); + } + + constexpr size_t GetIndex() const { return TypeIndex; } + constexpr bool IsValid() const { return GetIndex() != static_cast(INDEX_NONE); } + constexpr explicit operator bool() const { return GetIndex() != static_cast(INDEX_NONE); } + + template constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == I : false; } + template constexpr bool HoldsAlternative() const { return IsValid() ? GetIndex() == TAlternativeIndex::Value : false; } + + constexpr void* GetData() { return &Value; } + constexpr const void* GetData() const { return &Value; } + + template constexpr typename TAlternativeType::Type& GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< TAlternativeType::Type*>(&Value); } + template constexpr typename TAlternativeType::Type&& GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< TAlternativeType::Type*>(&Value)); } + template constexpr const typename TAlternativeType::Type& GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast::Type*>(&Value); } + template constexpr const typename TAlternativeType::Type&& GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast::Type*>(&Value)); } + + template constexpr T& GetValue() & { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast< T*>(&Value); } + template constexpr T&& GetValue() && { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< T*>(&Value)); } + template constexpr const T& GetValue() const& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return *reinterpret_cast(&Value); } + template constexpr const T&& GetValue() const&& { checkf(HoldsAlternative(), TEXT("It is an error to call GetValue() on an wrong TVariant. Please either check HoldsAlternative() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(&Value)); } + + template constexpr typename TAlternativeType::Type Get(typename TAlternativeType::Type&& DefaultValue) && { return HoldsAlternative() ? GetValue() : DefaultValue; } + template constexpr typename TAlternativeType::Type Get(typename TAlternativeType::Type&& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } + + template constexpr T Get(T&& DefaultValue) && { return HoldsAlternative() ? GetValue() : DefaultValue; } + template constexpr T Get(T&& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } + + template + constexpr auto Visit(F&& Func) & + { + using ReturnType = typename TCommonType::Type...>::Type; + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return ReturnType(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitLValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + template + constexpr auto Visit(F&& Func) && + { + using ReturnType = typename TCommonType::Type...>::Type; + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return ReturnType(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitRValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + template + constexpr auto Visit(F&& Func) const& + { + using ReturnType = typename TCommonType::Type...>::Type; + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return ReturnType(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitConstLValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + template + constexpr auto Visit(F&& Func) const&& + { + using ReturnType = typename TCommonType::Type...>::Type; + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return ReturnType(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitConstRValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + template + constexpr R Visit(F&& Func) & + { + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return R(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitLValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + template + constexpr R Visit(F&& Func) && + { + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return R(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitRValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + template + constexpr R Visit(F&& Func) const& + { + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return R(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitConstLValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + template + constexpr R Visit(F&& Func) const&& + { + checkf(IsValid(), "It is an error to call Visit() on an wrong TVariant. Please either check IsValid()."); + return R(NAMESPACE_PRIVATE::TVariantVisitHelper::VisitConstRValueFuncs[GetIndex()](Forward(Func), &Value)); + } + + constexpr void Reset() + { + if (GetIndex() == static_cast(INDEX_NONE)) return; + + FHelper::DestroyFuncs[GetIndex()](&Value); + + TypeIndex = static_cast(INDEX_NONE); + } + +private: + + using FHelper = NAMESPACE_PRIVATE::TVariantHelper; + + TAlignedUnion<1, Types...>::Type Value; + size_t TypeIndex; + + friend constexpr bool operator==(const TVariant& LHS, const TVariant& RHS) + { + if (LHS.GetIndex() != RHS.GetIndex()) return false; + if (LHS.IsValid() == false) return true; + return FHelper::EqualityOperatorFuncs[LHS.GetIndex()](&LHS.Value, &RHS.Value); + } + + friend constexpr bool operator!=(const TVariant& LHS, const TVariant& RHS) + { + if (LHS.GetIndex() != RHS.GetIndex()) return true; + if (LHS.IsValid() == false) return false; + return FHelper::InequalityOperatorFuncs[LHS.GetIndex()](&LHS.Value, &RHS.Value); + } + +}; + +template +constexpr bool operator==(const TVariant& LHS, const T& RHS) +{ + return LHS.template HoldsAlternative() ? LHS.template GetValue() == RHS : false; +} + +template +constexpr bool operator!=(const TVariant& LHS, const T& RHS) +{ + return LHS.template HoldsAlternative() ? LHS.template GetValue() != RHS : true; +} + +template +constexpr bool operator==(const T& LHS, const TVariant& RHS) +{ + return RHS.template HoldsAlternative() ? LHS == RHS.template GetValue() : false; +} + +template +constexpr bool operator!=(const T& LHS, const TVariant& RHS) +{ + return RHS.template HoldsAlternative() ? LHS != RHS.template GetValue() : true; +} + +template requires (true && ... && (TIsMoveConstructible::Value && TIsSwappable::Value)) +constexpr void Swap(TVariant& A, TVariant& B) +{ + if (!A && !B) return; + + if (A && !B) + { + B = MoveTemp(A); + A.Reset(); + return; + } + + if (B && !A) + { + A = MoveTemp(B); + B.Reset(); + return; + } + + TVariant Temp = MoveTemp(A); + A = MoveTemp(B); + B = MoveTemp(Temp); +} + +template struct TIsVariantSpecialization : FFalse { }; +template struct TIsVariantSpecialization> : FTrue { }; + +template requires TIsVariantSpecialization::Type>::Value +struct TVariantAlternativeType { using Type = typename TCopyCV::Type, typename TRemoveCVRef::Type::template TAlternativeType::Type>::Type; }; + +template requires TIsVariantSpecialization::Type>::Value +struct TVariantAlternativeIndex : VariantType::template TAlternativeIndex { }; + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h index fb1041a..4ee581a 100644 --- a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h +++ b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h @@ -11,6 +11,7 @@ void REDCRAFTUTILITY_API TestInvoke(); void REDCRAFTUTILITY_API TestReferenceWrapper(); void REDCRAFTUTILITY_API TestCompare(); void REDCRAFTUTILITY_API TestOptional(); +void REDCRAFTUTILITY_API TestVariant(); void REDCRAFTUTILITY_API TestMiscellaneous(); NAMESPACE_MODULE_END(Utility)