From 9cc7ac94803b781218a60a77a5236cc360ff88f0 Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Sat, 3 Dec 2022 23:14:25 +0800 Subject: [PATCH] refactor(templates): refactor TVariant::Visit to Visit --- .../Private/Testing/TemplatesTesting.cpp | 83 +++++-- .../Source/Public/Templates/Variant.h | 210 ++++++++++++------ 2 files changed, 215 insertions(+), 78 deletions(-) diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index bfcb3c7..0d71b0b 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -242,9 +242,64 @@ void TestVariant() } { -// TVariant TempA = false; -// TempA.Visit([](auto& A) { A = true; }); -// always_check(TempA.GetValue()); + using VariantType = TVariant; + VariantType TempArray[] = { 10, 15ll, 1.5 }; + + for(auto&& TempA : TempArray) + { + Visit( + [](auto&& A) + { + using T = TRemoveCVRef; + if constexpr (CSameAs) always_check(A == 10); + else if constexpr (CSameAs) always_check(A == 15ll); + else if constexpr (CSameAs) always_check(A == 1.5); + else always_check_no_entry(); + }, + TempA + ); + + VariantType TempB = Visit([](auto&& A) -> VariantType { return A + A; }, TempA); + + Visit( + [](auto&& A, auto&& B) + { + using T = TRemoveCVRef; + if constexpr (CSameAs) always_check(A == 10 && B == 20); + else if constexpr (CSameAs) always_check(A == 15ll && B == 30ll); + else if constexpr (CSameAs) always_check(A == 1.5 && B == 3.0); + else always_check_no_entry(); + }, + TempA, TempB + ); + + Visit([](auto&& A) { A *= 2; }, TempA); + + Visit( + [](auto&& A) + { + using T = TRemoveCVRef; + if constexpr (CSameAs) always_check(A == 20); + else if constexpr (CSameAs) always_check(A == 30ll); + else if constexpr (CSameAs) always_check(A == 3.0); + else always_check_no_entry(); + }, + TempA + ); + } + + for (auto&& TempA : TempArray) { + Visit( + TOverloaded + { + [](int32 A) { always_check(A == 20); }, + [](int64 A) { always_check(A == 30ll); }, + [](float64 A) { always_check(A == 3.0); }, + [](auto A) { always_check_no_entry(); }, + }, + TempA + ); + } } { @@ -272,10 +327,10 @@ void TestVariant() bool bIsLValue; bool bIsRValue; - auto TestQualifiers = [&bIsConst, &bIsLValue, &bIsRValue](auto&& Arg) -> int32 + auto TestQualifiers = [&bIsConst, &bIsLValue, &bIsRValue](auto&& A) -> int32 { - using T = decltype(Arg); - always_check(Arg == 10); + using T = decltype(A); + always_check(A == 10); always_check(CConst> == bIsConst); always_check(CLValueReference == bIsLValue); always_check(CRValueReference == bIsRValue); @@ -287,7 +342,7 @@ void TestVariant() bIsRValue = false; TVariant TempLA = 10; - auto ReturnLA = TempLA.Visit(TestQualifiers); + auto ReturnLA = Visit(TestQualifiers, TempLA); always_check((CSameAs)); bIsConst = true; @@ -295,7 +350,7 @@ void TestVariant() bIsRValue = false; const TVariant TempLB = TempLA; - auto ReturnLB = TempLB.Visit(TestQualifiers); + auto ReturnLB = Visit(TestQualifiers, TempLB); always_check((CSameAs)); bIsConst = false; @@ -303,7 +358,7 @@ void TestVariant() bIsRValue = true; TVariant TempRA = 10; - auto ReturnRA = MoveTemp(TempRA).Visit(TestQualifiers); + auto ReturnRA = Visit(TestQualifiers, MoveTemp(TempRA)); always_check((CSameAs)); bIsConst = true; @@ -311,7 +366,7 @@ void TestVariant() bIsRValue = true; const TVariant TempRB = TempLA; - auto ReturnRB = MoveTemp(TempRB).Visit(TestQualifiers); + auto ReturnRB = Visit(TestQualifiers, MoveTemp(TempRB)); always_check((CSameAs)); bIsConst = false; @@ -319,7 +374,7 @@ void TestVariant() bIsRValue = false; TVariant TempLC = 10; - auto ReturnLC = TempLC.Visit(TestQualifiers); + auto ReturnLC = Visit(TestQualifiers, TempLC); always_check((CSameAs)); bIsConst = true; @@ -327,7 +382,7 @@ void TestVariant() bIsRValue = false; const TVariant TempLD = TempLC; - auto ReturnLD = TempLD.Visit(TestQualifiers); + auto ReturnLD = Visit(TestQualifiers, TempLD); always_check((CSameAs)); bIsConst = false; @@ -335,7 +390,7 @@ void TestVariant() bIsRValue = true; TVariant TempRC = 10; - auto ReturnRC = MoveTemp(TempRC).Visit(TestQualifiers); + auto ReturnRC = Visit(TestQualifiers, MoveTemp(TempRC)); always_check((CSameAs)); bIsConst = true; @@ -343,7 +398,7 @@ void TestVariant() bIsRValue = true; const TVariant TempRD = TempLC; - auto ReturnRD = MoveTemp(TempRD).Visit(TestQualifiers); + auto ReturnRD = Visit(TestQualifiers, MoveTemp(TempRD)); always_check((CSameAs)); } diff --git a/Redcraft.Utility/Source/Public/Templates/Variant.h b/Redcraft.Utility/Source/Public/Templates/Variant.h index cc12518..5966c01 100644 --- a/Redcraft.Utility/Source/Public/Templates/Variant.h +++ b/Redcraft.Utility/Source/Public/Templates/Variant.h @@ -239,70 +239,6 @@ public: template constexpr decltype(auto) Get( T& DefaultValue) & { return HoldsAlternative() ? GetValue() : DefaultValue; } template constexpr decltype(auto) Get(const T& DefaultValue) const& { return HoldsAlternative() ? GetValue() : DefaultValue; } - template requires (true && ... && CInvocable) - FORCEINLINE decltype(auto) Visit(F&& Func) & - { - checkf(IsValid(), TEXT("It is an error to call Visit() on an wrong TVariant. Please either check IsValid().")); - - using ReturnType = TCommonType...>; - - using FInvokeImpl = ReturnType(*)(F&&, void*); - static constexpr FInvokeImpl InvokeImpl[] = { [](F&& Func, void* This) -> ReturnType { return InvokeResult(Forward(Func), *reinterpret_cast(This)); }... }; - - return InvokeImpl[GetIndex()](Forward(Func), &Value); - } - - template requires (true && ... && CInvocable) - FORCEINLINE decltype(auto) Visit(F&& Func) && - { - checkf(IsValid(), TEXT("It is an error to call Visit() on an wrong TVariant. Please either check IsValid().")); - - using ReturnType = TCommonType...>; - - using FInvokeImpl = ReturnType(*)(F&&, void*); - static constexpr FInvokeImpl InvokeImpl[] = { [](F&& Func, void* This) -> ReturnType { return InvokeResult(Forward(Func), MoveTemp(*reinterpret_cast(This))); }... }; - - return InvokeImpl[GetIndex()](Forward(Func), &Value); - } - - template requires (true && ... && CInvocable) - FORCEINLINE decltype(auto) Visit(F&& Func) const& - { - checkf(IsValid(), TEXT("It is an error to call Visit() on an wrong TVariant. Please either check IsValid().")); - - using ReturnType = TCommonType...>; - - using FInvokeImpl = ReturnType(*)(F&&, const void*); - static constexpr FInvokeImpl InvokeImpl[] = { [](F&& Func, const void* This) -> ReturnType { return InvokeResult(Forward(Func), *reinterpret_cast(This)); }... }; - - return InvokeImpl[GetIndex()](Forward(Func), &Value); - } - - template requires (true && ... && CInvocable) - FORCEINLINE decltype(auto) Visit(F&& Func) const&& - { - checkf(IsValid(), TEXT("It is an error to call Visit() on an wrong TVariant. Please either check IsValid().")); - - using ReturnType = TCommonType...>; - - using FInvokeImpl = ReturnType(*)(F&&, const void*); - static constexpr FInvokeImpl InvokeImpl[] = { [](F&& Func, const void* This) -> ReturnType { return InvokeResult(Forward(Func), MoveTemp(*reinterpret_cast(This))); }... }; - - return InvokeImpl[GetIndex()](Forward(Func), &Value); - } - - template requires (true && ... && CInvocableResult) - FORCEINLINE R Visit(F&& Func) & { return Visit(Forward(Func)); } - - template requires (true && ... && CInvocableResult) - FORCEINLINE R Visit(F&& Func) && { return MoveTemp(*this).Visit(Forward(Func)); } - - template requires (true && ... && CInvocableResult) - FORCEINLINE R Visit(F&& Func) const& { return Visit(Forward(Func)); } - - template requires (true && ... && CInvocableResult) - FORCEINLINE R Visit(F&& Func) const&& { return MoveTemp(*this).Visit(Forward(Func)); } - constexpr void Reset() { if (GetIndex() == INDEX_NONE) return; @@ -417,6 +353,152 @@ constexpr bool operator==(const TVariant& LHS, FInvalid) return !LHS.IsValid(); } +NAMESPACE_PRIVATE_BEGIN + +template +struct TVariantVisitImpl +{ + struct GetTotalNum + { + static constexpr size_t Do() + { + if (sizeof...(VariantTypes) == 0) return 0; + + constexpr size_t VariantNums[] = { TVariantNum>... }; + + size_t Result = 1; + + for (size_t Index = 0; Index < sizeof...(VariantTypes); ++Index) + { + Result *= VariantNums[Index]; + } + + return Result; + }; + }; + + struct EncodeIndices + { + static constexpr size_t Do(initializer_list Indices) + { + constexpr size_t VariantNums[] = { TVariantNum>... }; + + size_t Result = 0; + + for (size_t Index = 0; Index < sizeof...(VariantTypes); ++Index) + { + Result *= VariantNums[Index]; + Result += GetData(Indices)[Index]; + } + + return Result; + }; + }; + + struct DecodeExtent + { + static constexpr size_t Do(size_t EncodedIndex, size_t Extent) + { + constexpr size_t VariantNums[] = { TVariantNum>... }; + + for (size_t Index = Extent + 1; Index < sizeof...(VariantTypes); ++Index) + { + EncodedIndex /= VariantNums[Index]; + } + + return EncodedIndex % VariantNums[Extent]; + }; + }; + + template + struct InvokeEncoded; + + template + struct InvokeEncoded> + { + static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants) + { + return Invoke(Forward(Func), Forward(Variants).template GetValue()...); + } + + template + struct Result + { + static constexpr Ret Do(F&& Func, VariantTypes&&... Variants) + { + return InvokeResult(Forward(Func), Forward(Variants).template GetValue()...); + } + }; + }; + + template + struct InvokeVariant; + + template + struct InvokeVariant> + { + static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants) + { + using ExtentIndices = TIndexSequenceFor; + + using ResultType = TCommonType::Do(Forward(Func), Forward(Variants)...))...>; + + using InvokeImplType = ResultType(*)(F&&, VariantTypes&&...); + + constexpr InvokeImplType InvokeImpl[] = { InvokeEncoded::template Result::Do... }; + + return InvokeImpl[EncodeIndices::Do({ Variants.GetIndex()... })](Forward(Func), Forward(Variants)...); + } + + template + struct Result + { + static constexpr Ret Do(F&& Func, VariantTypes&&... Variants) + { + using ExtentIndices = TIndexSequenceFor; + + using InvokeImplType = Ret(*)(F&&, VariantTypes&&...); + + constexpr InvokeImplType InvokeImpl[] = { InvokeEncoded::template Result::Do... }; + + return InvokeImpl[EncodeIndices::Do({ Variants.GetIndex()... })](Forward(Func), Forward(Variants)...); + } + }; + }; + + static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants) + { + return InvokeVariant>::Do(Forward(Func), Forward(Variants)...); + } + + template + struct Result + { + static constexpr Ret Do(F&& Func, VariantTypes&&... Variants) + { + return InvokeVariant>::template Result::Do(Forward(Func), Forward(Variants)...); + } + }; +}; + +NAMESPACE_PRIVATE_END + +template + requires (CTVariant> && (true && ... && CTVariant>)) +constexpr decltype(auto) Visit(F&& Func, FirstVariantType&& FirstVariant, VariantTypes&&... Variants) +{ + checkf((true && ... && Variants.IsValid()), TEXT("It is an error to call Visit() on an wrong TVariant. Please either check IsValid().")); + return NAMESPACE_PRIVATE::TVariantVisitImpl::Do(Forward(Func), Forward(FirstVariant), Forward(Variants)...); +} + +template + requires (CTVariant> && (true && ... && CTVariant>)) +constexpr Ret Visit(F&& Func, FirstVariantType&& FirstVariant, VariantTypes&&... Variants) +{ + checkf((true && ... && Variants.IsValid()), TEXT("It is an error to call Visit() on an wrong TVariant. Please either check IsValid().")); + return NAMESPACE_PRIVATE::TVariantVisitImpl::template Result::Do(Forward(Func), Forward(FirstVariant), Forward(Variants)...); +} + NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END