Compare commits

..

No commits in common. "9dd5e747884da3cce7242899823bc0c4a9a37e96" and "bf2239712332ac27b74d44dfb4900788fe2e1592" have entirely different histories.

14 changed files with 1607 additions and 1860 deletions

View File

@ -2,10 +2,9 @@
#include "Strings/Char.h"
#include "Memory/Memory.h"
#include "Numerics/Numerics.h"
#include "Strings/String.h"
#include "Numerics/Numerics.h"
#include "Strings/StringView.h"
#include "Strings/Convert.h"
#include "Miscellaneous/AssertionMacros.h"
NAMESPACE_REDCRAFT_BEGIN
@ -197,11 +196,10 @@ void TestStringView()
{
always_check( LITERAL_VIEW(T, "012345678900").IsASCII());
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsASCII());
always_check( LITERAL_VIEW(T, "012345678900").template IsInteger<uint64>(10));
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").template IsInteger<uint64>(10));
always_check(!LITERAL_VIEW(T, "0123456789AB").template IsInteger<uint64>(10));
always_check( LITERAL_VIEW(T, "0123456789AB").template IsInteger<uint64>(16));
always_check( LITERAL_VIEW(T, "012345678900").IsInteger());
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsInteger());
always_check(!LITERAL_VIEW(T, "0123456789AB").IsInteger());
always_check( LITERAL_VIEW(T, "0123456789AB").IsInteger(16));
}
};
@ -213,7 +211,7 @@ void TestStringView()
Test(InPlaceType<unicodechar>);
}
void TestString()
void TestTemplateString()
{
auto Test = []<typename T>(TInPlaceType<T>)
{
@ -450,17 +448,38 @@ void TestString()
Test(InPlaceType<unicodechar>);
}
void TestConvert()
void TestStringConversion()
{
auto Test = []<typename T>(TInPlaceType<T>)
{
always_check(TString<T>::Format(LITERAL(T, "#{}#"), true ) == LITERAL(T, "#True#" ));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), false) == LITERAL(T, "#False#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +0) == LITERAL(T, "#0#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 0) == LITERAL(T, "#0#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -0) == LITERAL(T, "#0#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 42) == LITERAL(T, "#42#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +0.0) == LITERAL(T, "#0.000000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 0.0) == LITERAL(T, "#0.000000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -0.0) == LITERAL(T, "#-0.000000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 3.14) == LITERAL(T, "#3.140000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +TNumericLimits<float>::Infinity()) == LITERAL(T, "#Infinity#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -TNumericLimits<float>::Infinity()) == LITERAL(T, "#-Infinity#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#NaN#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#-NaN#"));
auto CheckParseArithmetic = []<typename U>(TStringView<T> View, U Result)
{
U Object;
if constexpr (CSameAs<U, bool>) always_check(View.Parse(Object));
else if constexpr (CIntegral<U>) always_check(View.Parse(Object));
else if constexpr (CFloatingPoint<U>) always_check(View.Parse(Object));
if constexpr (CSameAs<U, bool>) always_check(View.Parse(LITERAL(T, "{0:}"), Object) == 1);
else if constexpr (CIntegral<U>) always_check(View.Parse(LITERAL(T, "{0:+#I}"), Object) == 1);
else if constexpr (CFloatingPoint<U>) always_check(View.Parse(LITERAL(T, "{0:+#G}"), Object) == 1);
if constexpr (CFloatingPoint<U>)
{
@ -535,60 +554,6 @@ void TestConvert()
CheckParseFloat(InPlaceType<float>);
CheckParseFloat(InPlaceType<double>);
{
always_check( LITERAL_VIEW(T, "true" ).ToBool());
always_check(!LITERAL_VIEW(T, "false").ToBool());
always_check( LITERAL_VIEW(T, "True" ).ToBool());
always_check(!LITERAL_VIEW(T, "False").ToBool());
}
{
always_check(LITERAL_VIEW(T, "42" ).ToInt() == 42 );
always_check(LITERAL_VIEW(T, "FF" ).ToInt(16) == 255);
always_check(LITERAL_VIEW(T, "-42").ToInt() == -42);
always_check(LITERAL_VIEW(T, "0" ).ToInt() == 0 );
}
{
always_check(LITERAL_VIEW(T, "3.14" ).ToFloat() == 3.14f);
always_check(LITERAL_VIEW(T, "3.14e+00").ToFloat() == 3.14f);
always_check(LITERAL_VIEW(T, "-3.14" ).ToFloat() == -3.14f);
always_check(LITERAL_VIEW(T, "0.0" ).ToFloat() == 0.0f);
}
};
Test(InPlaceType<char>);
Test(InPlaceType<wchar>);
Test(InPlaceType<u8char>);
Test(InPlaceType<u16char>);
Test(InPlaceType<u32char>);
Test(InPlaceType<unicodechar>);
}
void TestStringConversion()
{
auto Test = []<typename T>(TInPlaceType<T>)
{
always_check(TString<T>::Format(LITERAL(T, "#{}#"), true ) == LITERAL(T, "#True#" ));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), false) == LITERAL(T, "#False#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +0) == LITERAL(T, "#0#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 0) == LITERAL(T, "#0#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -0) == LITERAL(T, "#0#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 42) == LITERAL(T, "#42#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +0.0) == LITERAL(T, "#0.000000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 0.0) == LITERAL(T, "#0.000000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -0.0) == LITERAL(T, "#-0.000000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), 3.14) == LITERAL(T, "#3.140000#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +TNumericLimits<float>::Infinity()) == LITERAL(T, "#Infinity#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -TNumericLimits<float>::Infinity()) == LITERAL(T, "#-Infinity#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), +TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#NaN#"));
always_check(TString<T>::Format(LITERAL(T, "#{}#"), -TNumericLimits<float>::QuietNaN()) == LITERAL(T, "#-NaN#"));
{
always_check(TString<T>::FromBool(true ) == LITERAL(T, "True" ));
always_check(TString<T>::FromBool(false) == LITERAL(T, "False"));
@ -614,6 +579,37 @@ void TestStringConversion()
always_check(TString<T>::FromFloat(3.14f, false, false, 2) == LITERAL(T, "1.92p+1" ));
always_check(TString<T>::FromFloat(1.0f / 3.0f, true, false, 5) == LITERAL(T, "0.33333" ));
}
{
always_check( LITERAL_VIEW(T, "True" ).ToBool());
always_check(!LITERAL_VIEW(T, "False" ).ToBool());
always_check( LITERAL_VIEW(T, "1" ).ToBool());
always_check(!LITERAL_VIEW(T, "0" ).ToBool());
always_check(!LITERAL_VIEW(T, "random").ToBool());
}
{
always_check(LITERAL_VIEW(T, "42" ).ToInt() == 42 );
always_check(LITERAL_VIEW(T, "FF" ).ToInt(16) == 255);
always_check(LITERAL_VIEW(T, "-42" ).ToInt() == -42);
always_check(LITERAL_VIEW(T, "0" ).ToInt() == 0 );
always_check(LITERAL_VIEW(T, "Invalid").ToInt() == 0 );
always_check(LITERAL_VIEW(T, "999999999999999999999999999999").ToInt() == 0);
always_check(LITERAL_VIEW(T, "-999999999999999999999999999999").ToInt() == 0);
}
{
always_check(LITERAL_VIEW(T, "3.14" ).ToFloat() == 3.14f);
always_check(LITERAL_VIEW(T, "3.14e+00").ToFloat() == 3.14f);
always_check(LITERAL_VIEW(T, "-3.14" ).ToFloat() == -3.14f);
always_check(LITERAL_VIEW(T, "0.0" ).ToFloat() == 0.0f);
always_check(Math::IsNaN(LITERAL_VIEW(T, "1e+308").ToFloat()));
always_check(Math::IsNaN(LITERAL_VIEW(T, "-1e+308").ToFloat()));
always_check(Math::IsNaN(LITERAL_VIEW(T, "1e-308").ToFloat()));
always_check(Math::IsNaN(LITERAL_VIEW(T, "-1e-308").ToFloat()));
}
};
Test(InPlaceType<char>);
@ -630,8 +626,7 @@ void TestString()
{
NAMESPACE_PRIVATE::TestChar();
NAMESPACE_PRIVATE::TestStringView();
NAMESPACE_PRIVATE::TestString();
NAMESPACE_PRIVATE::TestConvert();
NAMESPACE_PRIVATE::TestTemplateString();
NAMESPACE_PRIVATE::TestStringConversion();
}

View File

@ -1001,13 +1001,13 @@ void TestTuple()
{
TTuple<int32, char> TempA = { 1, 'A' };
TempA.Visit([](auto&& A) -> void { ++A; });
TempA.Visit([](auto&& A) { A++; });
TempA.Visit(
[]<typename T> (T&& A)
{
if constexpr (CSameAs<T&&, int32&>) always_check(A == 2 );
else if constexpr (CSameAs<T&&, char &>) always_check(A == 'B');
if constexpr (CSameAs<T&&, int32&>) always_check(A == 2);
else if constexpr (CSameAs<T&&, char&>) always_check(A == 'B');
else always_check_no_entry();
}
);

View File

@ -1209,7 +1209,7 @@ template <CForwardRange R,
CRegularInvocable<TRangeReference<R>> Proj =
decltype([]<typename T>(T&& A) -> T&& { return Forward<T>(A); }),
CEquivalenceRelation<TInvokeResult<Proj, TRangeReference<R>>, TInvokeResult<Proj, TRangeReference<R>>> Pred =
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TRangeReference<R>>, TInvokeResult<Proj, TRangeReference<R>>>,
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TRangeReference<R>>>,
decltype([]<typename LHS, typename RHS>(const LHS& A, const RHS& B) { return A == B; }), void>>
requires (CBorrowedRange<R>)
NODISCARD constexpr TRangeIterator<R> FindAdjacent(R&& Range, Pred Predicate = { }, Proj Projection = { })
@ -1251,7 +1251,7 @@ template <CForwardIterator I, CSentinelFor<I> S,
CRegularInvocable<TIteratorReference<I>> Proj =
decltype([]<typename T>(T&& A) -> T&& { return Forward<T>(A); }),
CEquivalenceRelation<TInvokeResult<Proj, TIteratorReference<I>>, TInvokeResult<Proj, TIteratorReference<I>>> Pred =
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TIteratorReference<I>>, TInvokeResult<Proj, TIteratorReference<I>>>,
TConditional<CWeaklyEqualityComparable<TInvokeResult<Proj, TIteratorReference<I>>>,
decltype([]<typename LHS, typename RHS>(const LHS& A, const RHS& B) { return A == B; }), void>>
NODISCARD FORCEINLINE constexpr I FindAdjacent(I First, S Last, Pred Predicate = { }, Proj Projection = { })
{

View File

@ -6,7 +6,6 @@
#include "Templates/Utility.h"
#include "Templates/Noncopyable.h"
#include "Templates/Invoke.h"
#include "Memory/Address.h"
#include "Miscellaneous/AssertionMacros.h"
NAMESPACE_REDCRAFT_BEGIN
@ -125,77 +124,27 @@ private:
static_assert(COutputIterator<TInsertIterator<void(*)(int)>, int>);
template <typename C>
class FFrontInserter
{
public:
FORCEINLINE constexpr explicit FFrontInserter(C& InContainer) : Container(AddressOf(InContainer)) { }
template <typename T>
FORCEINLINE constexpr void operator()(T&& A) { Container->PushFront(Forward<T>(A)); }
private:
C* Container;
};
template <typename C>
class FBackInserter
{
public:
FORCEINLINE constexpr explicit FBackInserter(C& InContainer) : Container(AddressOf(InContainer)) { }
template <typename T>
FORCEINLINE constexpr void operator()(T&& A) { Container->PushBack(Forward<T>(A)); }
private:
C* Container;
};
template <typename C>
class FInserter
{
public:
template <typename I>
FORCEINLINE constexpr FInserter(C& InContainer, I&& InIter) : Container(AddressOf(InContainer)), Iter(Forward<I>(InIter)) { }
template <typename T>
FORCEINLINE constexpr void operator()(T&& A) { Iter = Container->Insert(Iter, Forward<T>(A)); ++Iter; }
private:
C* Container;
typename C::FConstIterator Iter;
};
NAMESPACE_PRIVATE_END
/** Creates an iterator adapter inserted in the front of the container. */
template <typename C>
NODISCARD FORCEINLINE constexpr auto MakeFrontInserter(C& Container)
{
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FFrontInserter(Container));
return NAMESPACE_PRIVATE::TInsertIterator([&Container]<typename T>(T&& A) { Container.PushFront(Forward<T>(A)); });
}
/** Creates an iterator adapter inserted in the back of the container. */
template <typename C>
NODISCARD FORCEINLINE constexpr auto MakeBackInserter(C& Container)
{
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FBackInserter(Container));
return NAMESPACE_PRIVATE::TInsertIterator([&Container]<typename T>(T&& A) { Container.PushBack(Forward<T>(A)); });
}
/** Creates an iterator adapter inserted in the container. */
template <typename C, typename I>
NODISCARD FORCEINLINE constexpr auto MakeInserter(C& Container, I&& InIter)
template <typename C>
NODISCARD FORCEINLINE constexpr auto MakeInserter(C& Container, const typename C::FConstIterator& InIter)
{
return NAMESPACE_PRIVATE::TInsertIterator(NAMESPACE_PRIVATE::FInserter(Container, Forward<I>(InIter)));
return NAMESPACE_PRIVATE::TInsertIterator([&Container, Iter = InIter]<typename T>(T&& A) mutable { Iter = Container.Insert(Iter, Forward<T>(A)); ++Iter; });
}
NAMESPACE_MODULE_END(Utility)

View File

@ -32,50 +32,25 @@ concept CReservableContainer = CSizedRange<C>
&& requires (C& Container, size_t N)
{
Container.Reserve(N);
{ Container.Num() } -> CSameAs<size_t>;
{ Container.Max() } -> CSameAs<size_t>;
};
/** A concept specifies a container that can append elements. */
template <typename C, typename T>
template <typename C, typename Ref>
concept CAppendableContainer =
requires (C& Container, T&& Object)
requires (C& Container, Ref&& Reference)
{
requires
(
requires { Container.EmplaceBack (Forward<T>(Object)); } ||
requires { Container.PushBack (Forward<T>(Object)); } ||
requires { Container.Emplace(Container.End(), Forward<T>(Object)); } ||
requires { Container.Insert (Container.End(), Forward<T>(Object)); }
requires { Container.EmplaceBack (Forward<Ref>(Reference)); } ||
requires { Container.PushBack (Forward<Ref>(Reference)); } ||
requires { Container.Emplace(Container.End(), Forward<Ref>(Reference)); } ||
requires { Container.Insert (Container.End(), Forward<Ref>(Reference)); }
);
};
NAMESPACE_BEGIN(Ranges)
template <typename T, CAppendableContainer<T> C>
FORCEINLINE constexpr void AppendTo(C& Container, T&& Object)
{
if constexpr (requires { Container.EmplaceBack(Forward<T>(Object)); })
{
Container.EmplaceBack(Forward<T>(Object));
}
else if constexpr (requires { Container.PushBack(Forward<T>(Object)); })
{
Container.PushBack(Forward<T>(Object));
}
else if constexpr (requires { Container.Emplace(Container.End(), Forward<T>(Object)); })
{
Container.Emplace(Container.End(), Forward<T>(Object));
}
else /* if constexpr (requires { Container.Insert(Container.End(), Forward<T>(Object)); }) */
{
Container.Insert(Container.End(), Forward<T>(Object));
}
}
/** Constructs a non-view object from the elements of the range. */
template <typename C, CInputRange R, typename... Ts> requires (!CView<C>)
NODISCARD FORCEINLINE constexpr auto To(R&& Range, Ts&&... Args)
@ -103,7 +78,25 @@ NODISCARD FORCEINLINE constexpr auto To(R&& Range, Ts&&... Args)
for (TRangeReference<R> Element : Range)
{
Ranges::AppendTo(Result, Forward<TRangeReference<R>>(Element));
if constexpr (requires { Result.EmplaceBack(DeclVal<TRangeReference<R>>()); })
{
Result.EmplaceBack(Forward<TRangeReference<R>>(Element));
}
else if constexpr (requires { Result.PushBack(DeclVal<TRangeReference<R>>()); })
{
Result.PushBack(Forward<TRangeReference<R>>(Element));
}
else if constexpr (requires { Result.Emplace(Result.End(), DeclVal<TRangeReference<R>>()); })
{
Result.Emplace(Result.End(), Forward<TRangeReference<R>>(Element));
}
else /* if constexpr (requires { Result.Insert(Result.End(), DeclVal<TRangeReference<R>>()); }) */
{
Result.Insert(Result.End(), Forward<TRangeReference<R>>(Element));
}
}
return Result;

View File

@ -112,6 +112,8 @@ class TRangeView : public IBasicViewInterface<TRangeView<I, S>>
{
public:
using FElementType = TIteratorElement<I>;
FORCEINLINE constexpr TRangeView() requires (CDefaultConstructible<I>) = default;
FORCEINLINE constexpr TRangeView(I InFirst, S InLast) : First(MoveTemp(InFirst)), Last(InLast) { }

View File

@ -85,14 +85,7 @@ struct TChar
NODISCARD FORCEINLINE static constexpr bool IsValid(FCharType InChar)
{
if constexpr (TChar::IsASCII() && CSameAs<FCharType, char>)
{
if (0x00 <= InChar && InChar <= 0x7F) return true;
return false;
}
else if constexpr (CSameAs<FCharType, u8char>)
if constexpr (CSameAs<FCharType, u8char>)
{
if ((InChar & 0b10000000) == 0b00000000) return true;

File diff suppressed because it is too large Load Diff

View File

@ -1,487 +0,0 @@
#pragma once
#include "CoreTypes.h"
#include "TypeTraits/TypeTraits.h"
#include "Templates/Utility.h"
#include "Ranges/Utility.h"
#include "Numerics/Limits.h"
#include "Algorithms/Basic.h"
#include "Memory/Allocators.h"
#include "Memory/Address.h"
#include "Containers/Array.h"
#include "Strings/Char.h"
#include "Miscellaneous/AssertionMacros.h"
#include <charconv>
#pragma warning(push)
#pragma warning(disable : 4146)
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
template <typename R>
concept CStringRange = CInputRange<R> && CCharType<TRangeElement<R>>;
template <typename I>
concept CStringIterator = CInputIterator<I> && CCharType<TIteratorElement<I>>;
NAMESPACE_BEGIN(Algorithms)
/**
* Parses a boolean value from the given string range.
* Ignore leading and trailing spaces and case-insensitive.
*
* - "True" become true.
* - "False" become false.
*
* @param Range - The range of characters to parse.
* @param Value - The boolean value to parse.
*
* @return true if the value is successfully parsed, false otherwise.
*/
template <CStringRange R>
constexpr bool Parse(R&& Range, bool& Value)
{
using FCharTraits = TChar<TRangeElement<R>>;
if constexpr (CSizedRange<R&>)
{
checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range)."));
}
auto Iter = Ranges::Begin(Range);
auto Sent = Ranges::End (Range);
bool Result;
// Ignore leading spaces.
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter == Sent) return false;
// Parse the true value.
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 't') || *Iter == LITERAL(TRangeElement<R>, 'T')))
{
++Iter;
Result = true;
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'r') || *Iter == LITERAL(TRangeElement<R>, 'R'))) ++Iter; else return false;
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'u') || *Iter == LITERAL(TRangeElement<R>, 'U'))) ++Iter; else return false;
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'e') || *Iter == LITERAL(TRangeElement<R>, 'E'))) ++Iter; else return false;
}
// Parse the false value.
else if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'f') || *Iter == LITERAL(TRangeElement<R>, 'F')))
{
++Iter;
Result = false;
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'a') || *Iter == LITERAL(TRangeElement<R>, 'A'))) ++Iter; else return false;
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'l') || *Iter == LITERAL(TRangeElement<R>, 'L'))) ++Iter; else return false;
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 's') || *Iter == LITERAL(TRangeElement<R>, 'S'))) ++Iter; else return false;
if (Iter != Sent && (*Iter == LITERAL(TRangeElement<R>, 'e') || *Iter == LITERAL(TRangeElement<R>, 'E'))) ++Iter; else return false;
}
else return false;
// Ignore trailing spaces.
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter != Sent) return false;
Value = Result;
return true;
}
/**
* Parses a boolean value from the given string range.
* Ignore leading and trailing spaces and case-insensitive.
*
* - "True" become true.
* - "False" become false.
*
* @param First - The iterator of the range.
* @param Last - The sentinel of the range.
* @param Value - The boolean value to parse.
*
* @return true if the value is successfully parsed, false otherwise.
*/
template <CStringIterator I, CSentinelFor<I> S>
FORCEINLINE constexpr bool Parse(I First, S Last, bool& Value)
{
if constexpr (CSizedSentinelFor<S, I>)
{
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
}
return Algorithms::Parse(Ranges::View(MoveTemp(First), Last), Value);
}
/**
* Parses an integral value from the given string range.
* Ignore leading and trailing spaces and case-insensitive.
* If the ingeter value is unsigned, the negative sign causes the parsing to fail.
* Allow parsing base prefixes: "0x" for hexadecimal, "0b" for binary, and "0" for octal.
*
* @param Range - The range of characters to parse.
* @param Value - The integral value to parse.
* @param Base - The base of the number, between [2, 36], or 0 for auto-detect.
*
* @return true if the value is successfully parsed, false otherwise.
*/
template <CStringRange R, CIntegral T> requires (!CConst<T> && !CVolatile<T> && !CSameAs<T, bool>)
constexpr bool Parse(R&& Range, T& Value, uint Base = 0)
{
using FCharTraits = TChar<TRangeElement<R>>;
checkf(Base == 0 || (Base >= 2 && Base <= 36), TEXT("Illegal base. Please check the Base."));
if constexpr (CSizedRange<R&>)
{
checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range)."));
}
auto Iter = Ranges::Begin(Range);
auto Sent = Ranges::End (Range);
// Ignore leading spaces.
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter == Sent) return false;
bool bNegative = false;
// Parse the negative sign.
if constexpr (CSigned<T>)
{
if (*Iter == LITERAL(TRangeElement<R>, '-'))
{
bNegative = true;
++Iter;
}
}
// Parse the positive sign.
if (!bNegative && *Iter == LITERAL(TRangeElement<R>, '+')) ++Iter;
// Auto-detect the base.
if (Base == 0)
{
if (Iter == Sent) return false;
if (*Iter == LITERAL(TRangeElement<R>, '0'))
{
++Iter;
// Return zero if the string has only one zero.
if (Iter == Sent || FCharTraits::IsSpace(*Iter))
{
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter != Sent) return false;
Value = 0;
return true;
}
if (*Iter == LITERAL(TRangeElement<R>, 'x') || *Iter == LITERAL(TRangeElement<R>, 'X'))
{
Base = 16;
++Iter;
}
else if (*Iter == LITERAL(TRangeElement<R>, 'b') || *Iter == LITERAL(TRangeElement<R>, 'B'))
{
Base = 2;
++Iter;
}
else if (FCharTraits::IsDigit(*Iter, 8)) Base = 8;
else return false;
}
else Base = 10;
}
// Parse the base prefix.
else if (Base == 2 || Base == 16)
{
if (Iter == Sent) return false;
if (*Iter == LITERAL(TRangeElement<R>, '0'))
{
++Iter;
// Return zero if the string has only one zero.
if (Iter == Sent || FCharTraits::IsSpace(*Iter))
{
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter != Sent) return false;
Value = 0;
return true;
}
if (Base == 16 && (*Iter == LITERAL(TRangeElement<R>, 'x') || *Iter == LITERAL(TRangeElement<R>, 'X'))) ++Iter;
if (Base == 2 && (*Iter == LITERAL(TRangeElement<R>, 'b') || *Iter == LITERAL(TRangeElement<R>, 'B'))) ++Iter;
}
}
if (Iter == Sent) return false;
check(Base >= 2 && Base <= 36);
if (!FCharTraits::IsDigit(*Iter, Base)) return false;
using FUnsignedT = TMakeUnsigned<T>;
FUnsignedT LastValue = 0;
FUnsignedT Unsigned = 0;
do
{
uint Digit = FCharTraits::ToDigit(*Iter);
// Break if the char is not a digit.
if (Digit >= Base) break;
++Iter;
LastValue = Unsigned;
Unsigned = LastValue * Base + Digit;
// Fail if the value is overflowed.
if (Unsigned < LastValue) return false;
}
while (Iter != Sent);
// Ignore trailing spaces.
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter != Sent) return false;
if constexpr (CSigned<T>)
{
// Fail if the value is overflowed.
if (!bNegative && Unsigned >= static_cast<FUnsignedT>(TNumericLimits<T>::Max())) return false;
if ( bNegative && Unsigned >= static_cast<FUnsignedT>(TNumericLimits<T>::Min())) return false;
// Reverse if the value is negative.
if (bNegative) Unsigned = -Unsigned;
}
Value = Unsigned;
return true;
}
/**
* Parses an integral value from the given string range.
* Ignore leading and trailing spaces and case-insensitive.
* If the ingeter value is unsigned, the negative sign causes the parsing to fail.
* Allow parsing base prefixes: "0x" for hexadecimal, "0b" for binary, and "0" for octal.
*
* @param First - The iterator of the range.
* @param Last - The sentinel of the range.
* @param Value - The integral value to parse.
* @param Base - The base of the number, between [2, 36], or 0 for auto-detect.
*
* @return true if the value is successfully parsed, false otherwise.
*/
template <CStringIterator I, CSentinelFor<I> S, CIntegral T> requires (!CConst<T> && !CVolatile<T> && !CSameAs<T, bool>)
FORCEINLINE constexpr bool Parse(I First, S Last, T& Value, uint Base = 0)
{
if constexpr (CSizedSentinelFor<S, I>)
{
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
}
return Algorithms::Parse(Ranges::View(MoveTemp(First), Last), Value, Base);
}
/**
* Parses a floating-point value from the given string range.
* Ignore leading and trailing spaces and case-insensitive.
* Automatically detect formats if multiple formats are allowed.
* Allow parsing base prefixes: "0x" for hexadecimal.
*
* @param Range - The range of characters to parse.
* @param Value - The floating-point value to parse.
* @param bFixed - Allow parsing fixed-point values.
* @param bScientific - Allow parsing scientific notation values.
* @param bHex - Allow parsing hex floating-point values.
*
* @return true if the value is successfully parsed, false otherwise.
*/
template <CStringRange R, CFloatingPoint T> requires (!CConst<T> && !CVolatile<T>)
constexpr bool Parse(R&& Range, T& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
{
if (!bFixed && !bScientific && !bHex) return false;
using FCharTraits = TChar<TRangeElement<R>>;
if constexpr (CSizedRange<R&>)
{
checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range)."));
}
auto Iter = Ranges::Begin(Range);
auto Sent = Ranges::End (Range);
// Ignore leading spaces.
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter == Sent) return false;
bool bNegative = false;
// Parse the negative sign.
if (*Iter == LITERAL(TRangeElement<R>, '-'))
{
bNegative = true;
++Iter;
}
// Parse the positive sign.
else if (*Iter == LITERAL(TRangeElement<R>, '+')) ++Iter;
if (Iter == Sent) return false;
// Fail if the string has multiple signs.
if (*Iter == LITERAL(TRangeElement<R>, '-')) return false;
if (*Iter == LITERAL(TRangeElement<R>, '+')) return false;
NAMESPACE_STD::chars_format Format = NAMESPACE_STD::chars_format::general;
if ( bFixed && !bScientific) Format = NAMESPACE_STD::chars_format::fixed;
else if (!bFixed && bScientific) Format = NAMESPACE_STD::chars_format::scientific;
else if (!bFixed && !bScientific) Format = NAMESPACE_STD::chars_format::hex;
// Auto-detect the hex format.
if (bHex)
{
if (*Iter == LITERAL(TRangeElement<R>, '0'))
{
++Iter;
// Return zero if the string has only one zero.
if (Iter == Sent || FCharTraits::IsSpace(*Iter))
{
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter != Sent) return false;
Value = static_cast<T>(bNegative ? -0.0 : 0.0);
return true;
}
if (*Iter == LITERAL(TRangeElement<R>, 'x') || *Iter == LITERAL(TRangeElement<R>, 'X'))
{
Format = NAMESPACE_STD::chars_format::hex;
++Iter;
}
}
}
if (Iter == Sent) return false;
T Result;
// Copy to a buffer if the range is not contiguous.
if constexpr (!CContiguousRange<R> || !CSameAs<TRangeElement<R>, char>)
{
TArray<char, TInlineAllocator<64>> Buffer;
for (; Iter != Sent; ++Iter)
{
auto Char = *Iter;
// Ignore trailing spaces.
if (FCharTraits::IsSpace(Char)) break;
// Assert that floating-point values must be represented by ASCII.
if (FCharTraits::IsASCII(Char)) Buffer.PushBack(static_cast<char>(Char));
else return false;
}
const char* First = Buffer.GetData();
const char* Last = Buffer.GetData() + Buffer.Num();
NAMESPACE_STD::from_chars_result ConvertResult = NAMESPACE_STD::from_chars(First, Last, Result, Format);
if (ConvertResult.ec == NAMESPACE_STD::errc::result_out_of_range) return false;
if (ConvertResult.ec == NAMESPACE_STD::errc::invalid_argument) return false;
// Assert that the buffer is fully parsed.
if (ConvertResult.ptr != Last) return false;
}
else
{
const char* First = ToAddress(Iter);
const char* Last = ToAddress(Iter) + Algorithms::Distance(Iter, Sent);
NAMESPACE_STD::from_chars_result ConvertResult = NAMESPACE_STD::from_chars(First, Last, Result, Format);
if (ConvertResult.ec == NAMESPACE_STD::errc::result_out_of_range) return false;
if (ConvertResult.ec == NAMESPACE_STD::errc::invalid_argument) return false;
// Move the iterator to the end of the parsed value.
Algorithms::Advance(Iter, ConvertResult.ptr - First);
}
// Ignore trailing spaces.
while (Iter != Sent && FCharTraits::IsSpace(*Iter)) ++Iter;
if (Iter != Sent) return false;
Value = bNegative ? -Result : Result;
return true;
}
/**
* Parses a floating-point value from the given string range.
* Ignore leading and trailing spaces and case-insensitive.
* Automatically detect formats if multiple formats are allowed.
* Allow parsing base prefixes: "0x" for hexadecimal.
*
* @param First - The iterator of the range.
* @param Last - The sentinel of the range.
* @param Value - The floating-point value to parse.
* @param bFixed - Allow parsing fixed-point values.
* @param bScientific - Allow parsing scientific notation values.
* @param bHex - Allow parsing hex floating-point values.
*
* @return true if the value is successfully parsed, false otherwise.
*/
template <CStringIterator I, CSentinelFor<I> S, CFloatingPoint T> requires (!CConst<T> && !CVolatile<T>)
FORCEINLINE constexpr bool Parse(I First, S Last, T& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
{
if constexpr (CSizedSentinelFor<S, I>)
{
checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last."));
}
return Algorithms::Parse(Ranges::View(MoveTemp(First), Last), Value, bFixed, bScientific, bHex);
}
NAMESPACE_END(Algorithms)
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END
#pragma warning(pop)

View File

@ -1,990 +0,0 @@
#pragma once
#include "CoreTypes.h"
#include "TypeTraits/TypeTraits.h"
#include "Templates/Utility.h"
#include "Ranges/Utility.h"
#include "Ranges/View.h"
#include "Algorithms/Basic.h"
#include "Containers/StaticArray.h"
#include "Numerics/Math.h"
#include "Strings/Char.h"
#include "Miscellaneous/AssertionMacros.h"
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
/** A concept specifies a type is a format description string context. */
template <typename T, typename CharType = char>
concept CFormatStringContext = CInputRange<T> && CCharType<CharType> && CSameAs<TRangeElement<T>, CharType>
&& requires(T& Context, TRangeIterator<T> Iter, size_t Index)
{
/** Set the iterator of the context. */
Context.AdvanceTo(MoveTemp(Iter));
/** @return The next automatic index. */
{ Context.GetNextIndex() } -> CSameAs<size_t>;
/** @return true if the manual index is valid. */
{ Context.CheckIndex(Index) } -> CBooleanTestable;
};
/** A concept specifies a type is a format output context. */
template <typename T, typename CharType = char>
concept CFormatObjectContext = COutputRange<T, CharType> && CCharType<CharType>
&& requires(T& Context, TRangeIterator<T> Iter, size_t Index)
{
/** Set the iterator of the context. */
Context.AdvanceTo(MoveTemp(Iter));
/** Visit the format argument by index, and the argument is always like const&. */
{ Context.Visit([](const auto&) { return 0; }, Index) } -> CIntegral;
/** Visit the format argument by index, and the argument is always like const&. */
{ Context.template Visit<int>([](const auto&) { return 0; }, Index) } -> CIntegral;
};
/**
* A template class that defines the formatting rules for a specific type.
*
* @tparam T - The type of object being formatted.
* @tparam CharType - The character type of the formatting target.
*/
template <typename T, CCharType CharType = char> requires (CSameAs<TRemoveCVRef<T>, T>)
class TFormatter
{
public:
static_assert(sizeof(T) == -1, "The type is not formattable.");
FORCEINLINE constexpr TFormatter() = delete;
FORCEINLINE constexpr TFormatter(const TFormatter&) = delete;
FORCEINLINE constexpr TFormatter(TFormatter&&) = delete;
FORCEINLINE constexpr TFormatter& operator=(const TFormatter&) = delete;
FORCEINLINE constexpr TFormatter& operator=(TFormatter&&) = delete;
/**
* Parses the format description string from the context.
* Assert that the format description string is valid.
*
* @return The iterator that points to the first unmatched character.
*/
template <CFormatStringContext<CharType> CTX>
FORCEINLINE constexpr TRangeIterator<CTX> Parse(CTX& Context);
/**
* Formats the object and writes the result to the context.
* Do not assert that the output range is always large enough, and return directly if it is insufficient.
* Specify, unlike visiting arguments from the context which is always like const&,
* the object argument is a forwarding reference.
*
* @return The iterator that points to the next position of the output.
*/
template <typename U, CFormatObjectContext<CharType> CTX> requires (CSameAs<TRemoveCVRef<T>, U>)
FORCEINLINE constexpr TRangeIterator<CTX> Format(U&& Object, CTX& Context) const;
};
NAMESPACE_PRIVATE_BEGIN
template <typename I, typename S, typename... Ts>
class TFormatStringContext
{
public:
FORCEINLINE constexpr TFormatStringContext(I InFirst, S InLast) : First(MoveTemp(InFirst)), Last(InLast), AutomaticIndex(0) { }
NODISCARD FORCEINLINE constexpr I Begin() requires (!CCopyable<I>) { return MoveTemp(First); }
NODISCARD FORCEINLINE constexpr I Begin() const requires ( CCopyable<I>) { return First; }
NODISCARD FORCEINLINE constexpr S End() const { return Last; }
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedSentinelFor<S, I>) { return Last - First; }
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return First == Last; }
FORCEINLINE constexpr void AdvanceTo(I Iter) { First = MoveTemp(Iter); }
NODISCARD FORCEINLINE constexpr size_t GetNextIndex()
{
bool bIsValid = AutomaticIndex < sizeof...(Ts) && AutomaticIndex != INDEX_NONE;
checkf(bIsValid, TEXT("Illegal automatic indexing. Already entered manual indexing mode."));
if (!bIsValid) return INDEX_NONE;
return AutomaticIndex++;
}
NODISCARD FORCEINLINE constexpr bool CheckIndex(size_t Index)
{
bool bIsValid = AutomaticIndex == 0 || AutomaticIndex == INDEX_NONE;
checkf(bIsValid, TEXT("Illegal manual indexing. Already entered automatic indexing mode."));
if (!bIsValid) return false;
AutomaticIndex = INDEX_NONE;
return Index < sizeof...(Ts);
}
private:
NO_UNIQUE_ADDRESS I First;
NO_UNIQUE_ADDRESS S Last;
size_t AutomaticIndex = 0;
};
static_assert(CFormatStringContext<TFormatStringContext<IInputIterator<char>, ISentinelFor<IInputIterator<char>>>>);
template <typename I, typename S, typename... Ts>
class TFormatObjectContext
{
public:
FORCEINLINE constexpr TFormatObjectContext(I InFirst, S InLast, Ts&... Args) : First(MoveTemp(InFirst)), Last(InLast), ArgsTuple(Args...) { }
NODISCARD FORCEINLINE constexpr I Begin() requires (!CCopyable<I>) { return MoveTemp(First); }
NODISCARD FORCEINLINE constexpr I Begin() const requires ( CCopyable<I>) { return First; }
NODISCARD FORCEINLINE constexpr S End() const { return Last; }
NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedSentinelFor<S, I>) { return Last - First; }
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return First == Last; }
FORCEINLINE constexpr void AdvanceTo(I Iter) { First = MoveTemp(Iter); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, Ts&>) && CCommonReference<TInvokeResult<F&&, const Ts&>...>)
FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const { return ArgsTuple.Visit(Forward<F>(Func), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const Ts&>)
FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const { return ArgsTuple.template Visit<Ret>(Forward<F>(Func), Index); }
private:
NO_UNIQUE_ADDRESS I First;
NO_UNIQUE_ADDRESS S Last;
TTuple<Ts&...> ArgsTuple;
};
static_assert(CFormatObjectContext<TFormatObjectContext<IOutputIterator<char&>, ISentinelFor<IOutputIterator<char&>>, int>>);
NAMESPACE_PRIVATE_END
/** A concept specifies a type is formattable by the 'Algorithms::Format()'. */
template <typename T, typename CharType = char>
concept CFormattable = CCharType<CharType> && CSemiregular<TFormatter<TRemoveCVRef<T>, CharType>>
&& requires(TFormatter<TRemoveCVRef<T>, CharType>& Formatter, T& Object,
NAMESPACE_PRIVATE::TFormatStringContext< IInputIterator<CharType >, ISentinelFor< IInputIterator<CharType >>, T> FormatStringContext,
NAMESPACE_PRIVATE::TFormatObjectContext<IOutputIterator<CharType&>, ISentinelFor<IOutputIterator<CharType&>>, T> FormatObjectContext)
{
{ Formatter.Parse ( FormatStringContext) } -> CSameAs< IInputIterator<CharType >>;
{ Formatter.Format(Object, FormatObjectContext) } -> CSameAs<IOutputIterator<CharType&>>;
};
NAMESPACE_BEGIN(Algorithms)
/**
* Formats the objects and writes the result to the output range.
* Assert that the format description string is valid.
* If the output range is insufficient, return directly without asserting.
*
* @param Output - The output range to write the result.
* @param Fmt - The format description string.
* @param Args - The objects to format.
*
* @return The iterator that points to the next position of the output.
*/
template <CInputRange R1, COutputRange<TRangeElement<R1>> R2, CFormattable<TRangeElement<R1>>... Ts> requires (CBorrowedRange<R2>)
FORCEINLINE constexpr TRangeIterator<R2> Format(R2&& Output, R1&& Fmt, Ts&&... Args)
{
if constexpr (CSizedRange<R1&>)
{
checkf(Algorithms::Distance(Fmt) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Fmt)."));
}
if constexpr (CSizedRange<R2&>)
{
checkf(Algorithms::Distance(Output) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Output)."));
}
using FCharType = TRangeElement<R1>;
using FCharTraits = TChar<FCharType>;
using FFormatStringContext = NAMESPACE_PRIVATE::TFormatStringContext<TRangeIterator<R1>, TRangeSentinel<R1>, Ts...>;
using FFormatObjectContext = NAMESPACE_PRIVATE::TFormatObjectContext<TRangeIterator<R2>, TRangeSentinel<R2>, Ts...>;
FFormatStringContext FormatStringContext(Ranges::Begin(Fmt ), Ranges::End(Fmt ));
FFormatObjectContext FormatObjectContext(Ranges::Begin(Output), Ranges::End(Output), Args...);
auto FmtIter = Ranges::Begin(FormatStringContext);
auto FmtSent = Ranges::End (FormatStringContext);
auto OutIter = Ranges::Begin(FormatObjectContext);
auto OutSent = Ranges::End (FormatObjectContext);
// If the output range is insufficient.
if (OutIter == OutSent) UNLIKELY return OutIter;
TTuple<TFormatter<TRemoveCVRef<Ts>, FCharType>...> Formatters;
// For each character in the format string.
for (FCharType Char; FmtIter != FmtSent; ++FmtIter)
{
Char = *FmtIter;
// If the character may be a replacement field.
if (Char == LITERAL(FCharType, '{'))
{
if (++FmtIter == FmtSent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Unmatched '{' in format string."));
break;
}
Char = *FmtIter;
// If the character just an escaped '{'.
if (Char == LITERAL(FCharType, '{'))
{
if (OutIter == OutSent) UNLIKELY return OutIter;
*OutIter++ = LITERAL(FCharType, '{');
continue;
}
// If available replacement fields.
if constexpr (sizeof...(Ts) >= 1)
{
size_t Index;
// If the replacement field has a manual index.
if (Char != LITERAL(FCharType, ':') && Char != LITERAL(FCharType, '}'))
{
Index = 0;
bool bIsValid = true;
do
{
const uint Digit = FCharTraits::ToDigit(Char);
if (Digit > 10) bIsValid = false;
Index = Index * 10 + Digit;
if (++FmtIter == FmtSent) UNLIKELY break;
Char = *FmtIter;
}
while (Char != LITERAL(FCharType, ':') && Char != LITERAL(FCharType, '}'));
// If the index string contains illegal characters or the index is out of range.
if (!bIsValid || !FormatStringContext.CheckIndex(Index)) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the replacement field."));
break;
}
}
// If the replacement field need automatic indexing.
else
{
Index = FormatStringContext.GetNextIndex();
if (Index == INDEX_NONE)
{
checkf(false, TEXT("Illegal index. Please check the replacement field."));
break;
}
}
// Jump over the ':' character.
if (Char == LITERAL(FCharType, ':')) ++FmtIter;
FormatStringContext.AdvanceTo(MoveTemp(FmtIter));
// Parse the format description string.
FmtIter = Formatters.Visit([&FormatStringContext](auto& Formatter) -> decltype(FmtIter) { return Formatter.Parse(FormatStringContext); }, Index);
if (FmtIter == FmtSent || *FmtIter != LITERAL(FCharType, '}')) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
break;
}
FormatObjectContext.AdvanceTo(MoveTemp(OutIter));
auto FormatHandler = [&]<size_t... Indices>(TIndexSequence<Indices...>)
{
TTuple<TConstant<size_t, Indices>...> Visitor;
return Visitor.Visit([&]<size_t ConstantIndex>(TConstant<size_t, ConstantIndex>)
{
check(ConstantIndex == Index);
return Formatters.template GetValue<ConstantIndex>().Format(ForwardAsTuple(Forward<Ts>(Args)...).template GetValue<ConstantIndex>(), FormatObjectContext);
}
, Index);
};
// Format the object and write the result to the context.
OutIter = FormatHandler(TIndexSequenceFor<Ts...>());
}
else
{
checkf(false, TEXT("Illegal index. Please check the replacement field."));
break;
}
}
// If the character just an escaped '}'.
else if (Char == LITERAL(FCharType, '}'))
{
// Confirm the character is an escaped '}'.
if (++FmtIter != FmtSent && *FmtIter == LITERAL(FCharType, '}'))
{
if (OutIter == OutSent) UNLIKELY return OutIter;
*OutIter++ = LITERAL(FCharType, '}');
continue;
}
checkf(false, TEXT("Illegal format string. Missing '{' in format string."));
break;
}
// If the output range is insufficient.
else if (OutIter == OutSent) UNLIKELY return OutIter;
// If the character is not a replacement field.
else *OutIter++ = Char;
}
return OutIter;
}
/**
* Formats the objects and writes the result to the output range.
* Assert that the format description string is valid.
* If the output range is insufficient, return directly without asserting.
*
* @param OutputFirst - The iterator of the output range to write the result.
* @param OutputLast - The sentinel of the output range to write the result.
* @param FmtFirst - The iterator of the format description string.
* @param FmtLast - The sentinel of the format description string.
* @param Args - The objects to format.
*
* @return The iterator that points to the next position of the output.
*/
template <CInputIterator I1, CSentinelFor<I1> S1, COutputIterator<TIteratorElement<I1>> I2, CSentinelFor<I2> S2, CFormattable<TIteratorElement<I1>>... Ts>
FORCEINLINE constexpr I2 Format(I2 OutputFirst, S2 OutputLast, I1 FmtFirst, S1 FmtLast, Ts&&... Args)
{
if constexpr (CSizedSentinelFor<S1, I1>)
{
checkf(FmtFirst - FmtLast <= 0, TEXT("Illegal range iterator. Please check HaystackFirst <= HaystackLast."));
}
if constexpr (CSizedSentinelFor<S2, I2>)
{
checkf(OutputFirst - OutputLast <= 0, TEXT("Illegal range iterator. Please check NeedleFirst <= NeedleLast."));
}
return Algorithms::Format(Ranges::View(MoveTemp(OutputFirst), OutputLast), Ranges::View(MoveTemp(FmtFirst), FmtLast), Forward<Ts>(Args)...);
}
NAMESPACE_END(Algorithms)
/**
* A formatter the null-terminated string.
*
* The syntax of format specifications is:
*
* [Fill And Align] [Width] [Precision] [Type]
*
* 1. The fill and align part:
*
* [Fill Character] <Align Option>
*
* i. Fill Character: The character is used to fill width of the object. It is optional and cannot be '{' or '}'.
* It should be representable as a single unicode otherwise it is undefined behavior.
*
* ii. Align Option: The character is used to indicate the direction of alignment.
*
* - '<': Align the formatted argument to the left of the available space
* by inserting n fill characters after the formatted argument.
* - '^': Align the formatted argument to the center of the available space
* by inserting n fill characters around the formatted argument.
* If cannot absolute centering, offset to the left.
* - '>': Align the formatted argument ro the right of the available space
* by inserting n fill characters before the formatted argument.
*
* 2. The width part:
*
* - 'N': The number is used to specify the minimum field width of the object.
* N should be an unsigned non-zero decimal number.
* - '{N}': Dynamically determine the minimum field width of the object.
* N should be a valid index of the format integer argument.
* N is optional, and the default value is automatic indexing.
*
* 3. The precision part:
*
* - '.N': The number is used to specify the maximum field width of the object.
* N should be an unsigned non-zero decimal number.
* - '.{N}': Dynamically determine the maximum field width of the object.
* N should be a valid index of the format integer argument.
* N is optional, and the default value is automatic indexing.
*
* 4. The type indicators part:
*
* - none: Indicates the as-is formatting.
* - 'S': Indicates the as-is formatting.
* - '!': Indicates uppercase formatting.
* - 's': Indicates lowercase formatting.
* - '?': Indicates escape formatting.
*/
template <CCharType T>
class TFormatter<T*, T>
{
private:
using FCharType = T;
using FCharTraits = TChar<FCharType>;
using FFillCharacter = TStaticArray<FCharType, FCharTraits::MaxCodeUnitLength>;
public:
template <CFormatStringContext<FCharType> CTX>
constexpr TRangeIterator<CTX> Parse(CTX& Context)
{
auto Iter = Ranges::Begin(Context);
auto Sent = Ranges::End (Context);
// Set the default values.
{
FillUnitLength = 1;
FillCharacter[0] = LITERAL(FCharType, ' ');
AlignOption = LITERAL(FCharType, '<');
MinFieldWidth = 0;
MaxFieldWidth = -1;
bDynamicMin = false;
bDynamicMax = false;
bLowercase = false;
bUppercase = false;
bEscape = false;
}
// If the format description string is empty.
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
FCharType Char = *Iter; ++Iter;
// Try to parse the fill and align part.
// This code assumes that the format string does not contain multi-unit characters, except for fill character.
// If the fill character is multi-unit.
if (!FCharTraits::IsValid(Char))
{
FillUnitLength = 1;
FillCharacter[0] = Char;
while (true)
{
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
Char = *Iter; ++Iter;
// If the fill character ends.
if (FillUnitLength == FCharTraits::MaxCodeUnitLength || FCharTraits::IsValid(Char)) break;
FillCharacter[FillUnitLength++] = Char;
}
if (Char != LITERAL(FCharType, '<') && Char != LITERAL(FCharType, '^') && Char != LITERAL(FCharType, '>')) UNLIKELY
{
checkf(false, TEXT("Illegal format string. The fill character is not representable as a single unicode."));
return Iter;
}
AlignOption = Char;
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
// If the fill character is single-unit.
else do
{
if (Iter == Sent) break;
if (*Iter != LITERAL(FCharType, '<') && *Iter != LITERAL(FCharType, '^') && *Iter != LITERAL(FCharType, '>')) break;
FillUnitLength = 1;
FillCharacter[0] = Char;
Char = *Iter; ++Iter;
AlignOption = Char;
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
while (false);
// Try to parse the width part.
{
if (Char == LITERAL(FCharType, '{'))
{
bDynamicMin = true;
MinFieldWidth = INDEX_NONE;
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
Char = *Iter; ++Iter;
}
if ((bDynamicMin || Char != LITERAL(FCharType, '0')) && FCharTraits::IsDigit(Char))
{
MinFieldWidth = FCharTraits::ToDigit(Char);
while (true)
{
if (Iter == Sent)
{
checkf(!bDynamicMin, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
if (!bDynamicMin && *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
const uint Digit = FCharTraits::ToDigit(Char);
if (Digit > 10) break;
MinFieldWidth = MinFieldWidth * 10 + Digit;
}
}
if (bDynamicMin)
{
if (Char != LITERAL(FCharType, '}')) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
do
{
// Try to automatic indexing.
if (MinFieldWidth == INDEX_NONE)
{
MinFieldWidth = Context.GetNextIndex();
if (MinFieldWidth == INDEX_NONE) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the field width."));
}
else break;
}
// Try to manual indexing.
else if (!Context.CheckIndex(MinFieldWidth)) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the field width."));
}
else break;
bDynamicMin = false;
MinFieldWidth = 0;
}
while (false);
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
}
// Try to parse the precision part.
if (Char == LITERAL(FCharType, '.'))
{
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing precision in format string."));
return Iter;
}
Char = *Iter; ++Iter;
if (Char == LITERAL(FCharType, '{'))
{
bDynamicMax = true;
MaxFieldWidth = INDEX_NONE;
if (Iter == Sent) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
Char = *Iter; ++Iter;
}
if ((bDynamicMax || Char != LITERAL(FCharType, '0')) && FCharTraits::IsDigit(Char))
{
MaxFieldWidth = FCharTraits::ToDigit(Char);
while (true)
{
if (Iter == Sent)
{
checkf(!bDynamicMax, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
if (!bDynamicMax && *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
const uint Digit = FCharTraits::ToDigit(Char);
if (Digit > 10) break;
MaxFieldWidth = MaxFieldWidth * 10 + Digit;
}
}
else if (!bDynamicMax)
{
checkf(false, TEXT("Illegal format string. Missing precision in format string."));
return Iter;
}
if (bDynamicMax)
{
if (Char != LITERAL(FCharType, '}')) UNLIKELY
{
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
do
{
// Try to automatic indexing.
if (MaxFieldWidth == INDEX_NONE)
{
MaxFieldWidth = Context.GetNextIndex();
if (MaxFieldWidth == INDEX_NONE) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the precision."));
}
else break;
}
// Try to manual indexing.
else if (!Context.CheckIndex(MaxFieldWidth)) UNLIKELY
{
checkf(false, TEXT("Illegal index. Please check the precision."));
}
else break;
bDynamicMax = false;
MaxFieldWidth = -1;
}
while (false);
if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter;
Char = *Iter; ++Iter;
}
}
// Try to parse the type indicators part.
if (Char == LITERAL(FCharType, 'S')) { if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter; Char = *Iter; ++Iter; }
else if (Char == LITERAL(FCharType, '!')) { bUppercase = true; if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter; Char = *Iter; ++Iter; }
else if (Char == LITERAL(FCharType, 's')) { bLowercase = true; if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter; Char = *Iter; ++Iter; }
else if (Char == LITERAL(FCharType, '?')) { bEscape = true; if (Iter == Sent || *Iter == LITERAL(FCharType, '}')) return Iter; Char = *Iter; ++Iter; }
checkf(false, TEXT("Illegal format string. Missing '}' in format string."));
return Iter;
}
template <CFormatObjectContext<FCharType> CTX>
constexpr TRangeIterator<CTX> Format(const FCharType* Object, CTX& Context) const
{
auto Iter = Ranges::Begin(Context);
auto Sent = Ranges::End (Context);
size_t MinDynamicField = MinFieldWidth;
size_t MaxDynamicField = MaxFieldWidth;
if (bDynamicMin) MinDynamicField = Context.Visit([]<typename U>(U&& Value) -> size_t
{
if constexpr (CIntegral<U>)
{
checkf(Value > 0, TEXT("Illegal format argument. The dynamic width argument must be a unsigned non-zero number."));
return Math::Max(Value, 1);
}
else
{
checkf(false, TEXT("Illegal format argument. The dynamic width argument must be an integral."));
return 0;
}
}
, MinFieldWidth);
if (bDynamicMax) MaxDynamicField = Context.Visit([]<typename U>(U&& Value) -> size_t
{
if constexpr (CIntegral<U>)
{
checkf(Value > 0, TEXT("Illegal format argument. The dynamic precision argument must be a unsigned non-zero number."));
return Math::Max(Value, 1);
}
else
{
checkf(false, TEXT("Illegal format argument. The dynamic precision argument must be an integral."));
return 0;
}
}
, MaxFieldWidth);
size_t LeftPadding = 0;
size_t RightPadding = 0;
// Estimate the field width.
if (MinDynamicField != 0)
{
// If escape formatting is enabled, add quotes characters.
size_t FieldWidth = bEscape ? 2 : 0;
for (const FCharType* Ptr = Object; *Ptr != LITERAL(FCharType, '\0'); ++Ptr)
{
if (bEscape)
{
switch (const FCharType Char = *Ptr)
{
case LITERAL(FCharType, '\"'): FieldWidth += 2; break;
case LITERAL(FCharType, '\\'): FieldWidth += 2; break;
case LITERAL(FCharType, '\a'): FieldWidth += 2; break;
case LITERAL(FCharType, '\b'): FieldWidth += 2; break;
case LITERAL(FCharType, '\f'): FieldWidth += 2; break;
case LITERAL(FCharType, '\n'): FieldWidth += 2; break;
case LITERAL(FCharType, '\r'): FieldWidth += 2; break;
case LITERAL(FCharType, '\t'): FieldWidth += 2; break;
case LITERAL(FCharType, '\v'): FieldWidth += 2; break;
default:
{
// Use '\x00' format for other non-printable characters.
if (!FCharTraits::IsASCII(Char) || !FCharTraits::IsPrint(Char))
{
FieldWidth += 2 + sizeof(FCharType) * 2;
}
else ++FieldWidth;
}
}
}
else ++FieldWidth;
}
const size_t PaddingWidth = MinDynamicField - Math::Min(FieldWidth, MinDynamicField, MaxDynamicField);
if (AlignOption == LITERAL(FCharType, '<')) RightPadding = PaddingWidth;
else if (AlignOption == LITERAL(FCharType, '>')) LeftPadding = PaddingWidth;
else if (AlignOption == LITERAL(FCharType, '^'))
{
LeftPadding = Math::DivAndFloor(PaddingWidth, 2);
RightPadding = PaddingWidth - LeftPadding;
}
}
// Write the left padding.
for (size_t Index = 0; Index != LeftPadding; ++Index)
{
for (size_t Jndex = 0; Jndex != FillUnitLength; ++Jndex)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = FillCharacter[Jndex];
}
}
// Write the left quote.
if (bEscape)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = LITERAL(FCharType, '\"');
}
const FCharType* Ptr = Object - 1;
// Write the object, include escaped quotes in the counter.
for (size_t Index = bEscape ? 1 : 0; Index != MaxDynamicField; ++Index)
{
FCharType Char = *++Ptr;
if (Char == LITERAL(FCharType, '\0')) break;
if (Iter == Sent) UNLIKELY return Iter;
// Convert the character case.
if (bLowercase) Char = FCharTraits::ToLower(Char);
else if (bUppercase) Char = FCharTraits::ToUpper(Char);
if (bEscape)
{
switch (Char)
{
case LITERAL(FCharType, '\"'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, '\"'); break;
case LITERAL(FCharType, '\\'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, '\\'); break;
case LITERAL(FCharType, '\a'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'a'); break;
case LITERAL(FCharType, '\b'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'b'); break;
case LITERAL(FCharType, '\f'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'f'); break;
case LITERAL(FCharType, '\n'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'n'); break;
case LITERAL(FCharType, '\r'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'r'); break;
case LITERAL(FCharType, '\t'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 't'); break;
case LITERAL(FCharType, '\v'): *Iter++ = LITERAL(FCharType, '\\'); *Iter++ = LITERAL(FCharType, 'v'); break;
default:
{
// Use '\x00' format for other non-printable characters.
if (!FCharTraits::IsASCII(Char) || !FCharTraits::IsPrint(Char))
{
*Iter++ = LITERAL(FCharType, '\\');
*Iter++ = LITERAL(FCharType, 'x' );
using FUnsignedT = TMakeUnsigned<FCharType>;
constexpr size_t DigitNum = sizeof(FCharType) * 2;
FUnsignedT IntValue = static_cast<FUnsignedT>(Char);
FCharType Buffer[DigitNum];
for (size_t Jndex = 0; Jndex != DigitNum; ++Jndex)
{
Buffer[DigitNum - Jndex - 1] = FCharTraits::FromDigit(IntValue & 0xF);
IntValue >>= 4;
}
check(IntValue == 0);
for (size_t Jndex = 0; Jndex != DigitNum; ++Jndex)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = Buffer[Jndex];
}
}
else *Iter++ = Char;
}
}
}
else *Iter++ = Char;
}
// Write the right quote, if the field width is enough.
if (bEscape && *Ptr == LITERAL(FCharType, '\0'))
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = LITERAL(FCharType, '\"');
}
// Write the right padding.
for (size_t Index = 0; Index != RightPadding; ++Index)
{
for (size_t Jndex = 0; Jndex != FillUnitLength; ++Jndex)
{
if (Iter == Sent) UNLIKELY return Iter;
*Iter++ = FillCharacter[Jndex];
}
}
return Iter;
}
private:
size_t FillUnitLength = 1;
FFillCharacter FillCharacter = { LITERAL(FCharType, ' ') };
FCharType AlignOption = LITERAL(FCharType, '<');
size_t MinFieldWidth = 0;
size_t MaxFieldWidth = -1;
bool bDynamicMin = false;
bool bDynamicMax = false;
bool bLowercase = false;
bool bUppercase = false;
bool bEscape = false;
};
template <CCharType T> class TFormatter<const T* , T> : public TFormatter<T*, T> { };
template <CCharType T, size_t N> class TFormatter< T[N], T> : public TFormatter<T*, T> { };
template <CCharType T, size_t N> class TFormatter<const T[N], T> : public TFormatter<T*, T> { };
static_assert(CFormattable< char*>);
static_assert(CFormattable<const char*>);
static_assert(CFormattable< char[256]>);
static_assert(CFormattable<const char[256]>);
NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END

View File

@ -1144,64 +1144,22 @@ public:
return TStringView<FElementType>(*this).IsASCII();
}
/** @return true if the string can be converted to a boolean value, false otherwise. */
/** @return true if the string can be fully represented as a boolean value, false otherwise. */
NODISCARD FORCEINLINE bool IsBoolean() const
{
return TStringView<FElementType>(*this).IsBoolean();
}
/** @return true if the string can be converted to an integer value, false otherwise. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE bool IsInteger(uint Base = 0) const
/** @return true if the string can be fully represented as an integer value, false otherwise. */
NODISCARD FORCEINLINE bool IsInteger(unsigned Base = 10, bool bSigned = true) const
{
return TStringView<FElementType>(*this).template IsInteger<U>(Base);
return TStringView<FElementType>(*this).IsInteger(Base, bSigned);
}
/** @return true if the string can be converted to a floating-point value, false otherwise. */
template <CFloatingPoint U = float> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bHex = true) const
/** @return true if the string can be fully represented as a floating-point value, false otherwise. */
NODISCARD FORCEINLINE bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bSigned = true) const
{
return TStringView<FElementType>(*this).template IsFloatingPoint<U>(bFixed, bScientific, bHex);
}
/** Converts the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool ToBool() const
{
return TStringView<FElementType>(*this).ToBool();
}
/** Converts the string into an integer value. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr U ToInt(uint Base = 0) const
{
return TStringView<FElementType>(*this).template ToInt<U>(Base);
}
/** Converts the string into a floating-point value. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr U ToFloat(bool bFixed = true, bool bScientific = true, bool bHex = true) const
{
return TStringView<FElementType>(*this).template ToFloat<U>(bFixed, bScientific, bHex);
}
/** Parse the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool Parse(bool& Value)
{
return TStringView<FElementType>(*this).Parse(Value);
}
/** Parse the string into an integer value. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, uint Base = 0)
{
return TStringView<FElementType>(*this).Parse(Value, Base);
}
/** Parse the string into a floating-point value. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
{
return TStringView<FElementType>(*this).Parse(Value, bFixed, bScientific, bHex);
return TStringView<FElementType>(*this).IsFloatingPoint(bFixed, bScientific, bSigned);
}
public:
@ -1317,6 +1275,105 @@ public:
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
void AppendFloat(U Value, bool bFixed, bool bScientific, unsigned Precision);
/**
* Converts a string into a boolean value.
*
* - "True" and non-zero integers become true.
* - "False" and unparsable values become false.
*
* @return The boolean value.
*/
NODISCARD FORCEINLINE bool ToBool() const
{
return TStringView<FElementType>(*this).ToBool();
}
/**
* Converts a string into an integer value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - Only the minus sign is recognized (not the plus sign), and only for signed integer types of value.
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if IsNumeric(Base, false, true, false) is true.
*
* @param Base - The base of the number, between [2, 36].
*
* @return The integer value.
*/
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToInt(unsigned Base = 10) const
{
checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base."));
return TStringView<FElementType>(*this).template ToInt<U>(Base);
}
/**
* Converts a string into a floating-point value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - The plus sign is not recognized outside the exponent (only the minus sign is permitted at the beginning).
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if bFixed and IsNumeric(10, false) is true.
* Parsers hex floating-point values if bFixed and bScientific are false.
*
* @param bFixed - The fixed-point format.
* @param bScientific - The scientific notation.
*
* @return The floating-point value.
*/
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToFloat(bool bFixed = true, bool bScientific = false) const
{
return TStringView<FElementType>(*this).template ToFloat<U>(bFixed, bScientific);
}
/** Converts a string into a boolean value and remove the parsed substring. */
NODISCARD FORCEINLINE bool ToBoolAndTrim()
{
TStringView<FElementType> View = *this;
bool Result = View.ToBoolAndTrim();
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
}
/** Converts a string into an integer value and remove the parsed substring. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToIntAndTrim(unsigned Base = 10)
{
TStringView<FElementType> View = *this;
U Result = View.template ToIntAndTrim<U>(Base);
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
}
/** Converts a string into a floating-point value and remove the parsed substring. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE U ToFloatAndTrim(bool bFixed = true, bool bScientific = true)
{
TStringView<FElementType> View = *this;
U Result = View.template ToFloatAndTrim<U>(bFixed, bScientific);
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
}
public:
/**
@ -1341,6 +1398,35 @@ public:
template <typename... Ts>
void AppendFormat(TStringView<FElementType> Fmt, const Ts&... Args);
/**
* Parse a string using a format string to objects.
*
* @param Fmt - The format string.
* @param Args - The objects to parse.
*
* @return The number of objects successfully parsed.
*/
template <typename... Ts>
FORCEINLINE size_t Parse(TStringView<FElementType> Fmt, Ts&... Args) const
{
return TStringView(*this).Parse(Fmt, Args...);
}
/** Parse a string using a format string to objects and remove the parsed substring. */
template <typename... Ts>
FORCEINLINE size_t ParseAndTrim(TStringView<FElementType> Fmt, Ts&... Args)
{
TStringView<FElementType> View = *this;
size_t Result = View.ParseAndTrim(Fmt, Args...);
size_t TrimNum = this->Num() - View.Num();
if (TrimNum > 0) Erase(0, TrimNum);
return Result;
}
public:
/** Overloads the GetTypeHash algorithm for TString. */

View File

@ -12,7 +12,6 @@
#include "Iterators/BasicIterator.h"
#include "Iterators/Sentinel.h"
#include "Strings/Char.h"
#include "Strings/Convert.h"
#include "Miscellaneous/AssertionMacros.h"
#include <cstring>
@ -530,83 +529,133 @@ public:
return true;
}
/** @return true if the string can be converted to a boolean value, false otherwise. */
/** @return true if the string can be fully represented as a boolean value, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsBoolean() const
{
bool Temp;
TStringView View = *this;
return Algorithms::Parse(*this, Temp);
Ignore = View.ToBoolAndTrim();
return View.IsEmpty();
}
/** @return true if the string can be converted to an integer value, false otherwise. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE constexpr bool IsInteger(uint Base = 0) const
/** @return true if the string can be fully represented as an integer value, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsInteger(unsigned Base = 10, bool bSigned = true) const
{
U Temp;
TStringView View = *this;
return Algorithms::Parse(*this, Temp, Base);
}
/** @return true if the string can be converted to a floating-point value, false otherwise. */
template <CFloatingPoint U = float> requires (!CSameAs<U, bool> && CSameAs<TRemoveCVRef<U>, U>)
NODISCARD FORCEINLINE constexpr bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bHex = true) const
if (View.StartsWith(LITERAL(FElementType, '-')))
{
U Temp;
return Algorithms::Parse(*this, Temp, bFixed, bScientific, bHex);
if (bSigned) View.RemovePrefix(1);
else return false;
}
/** Converts the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool ToBool() const
Ignore = View.ToIntAndTrim(Base);
return View.IsEmpty();
}
/** @return true if the string can be fully represented as a floating-point value, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsFloatingPoint(bool bFixed = true, bool bScientific = true, bool bSigned = true) const
{
bool Result;
TStringView View = *this;
verifyf(Algorithms::Parse(*this, Result), TEXT("Illegal conversion. Please check the IsBoolean()."));
return Result;
if (View.StartsWith(LITERAL(FElementType, '-')))
{
if (bSigned) View.RemovePrefix(1);
else return false;
}
/** Converts the string into an integer value. */
Ignore = View.ToFloatAndTrim(bFixed, bScientific);
return View.IsEmpty();
}
public:
/**
* Converts a string into a boolean value.
*
* - "True" and non-zero integers become true.
* - "False" and unparsable values become false.
*
* @return The boolean value.
*/
NODISCARD constexpr bool ToBool() const
{
return TStringView(*this).ToBoolAndTrim();
}
/**
* Converts a string into an integer value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - Only the minus sign is recognized (not the plus sign), and only for signed integer types of value.
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if IsNumeric(Base, false, true, false) is true.
*
* @param Base - The base of the number, between [2, 36].
*
* @return The integer value.
*/
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr U ToInt(uint Base = 0) const
NODISCARD constexpr U ToInt(unsigned Base = 10) const
{
U Result;
verifyf(Algorithms::Parse(*this, Result, Base), TEXT("Illegal conversion. Please check the IsInteger()."));
return Result;
return TStringView(*this).ToIntAndTrim<U>(Base);
}
/** Converts the string into a floating-point value. */
/**
* Converts a string into a floating-point value.
*
* - "0x" or "0X" prefixes are not recognized if base is 16.
* - The plus sign is not recognized outside the exponent (only the minus sign is permitted at the beginning).
* - Leading whitespace is not ignored.
*
* Ensure that the entire string can be parsed if bFixed and IsNumeric(10, false) is true.
* Parsers hex floating-point values if bFixed and bScientific are false.
*
* @param bFixed - The fixed-point format.
* @param bScientific - The scientific notation.
*
* @return The floating-point value.
*/
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr U ToFloat(bool bFixed = true, bool bScientific = true, bool bHex = true) const
NODISCARD constexpr U ToFloat(bool bFixed = true, bool bScientific = true) const
{
U Result;
verifyf(Algorithms::Parse(*this, Result, bFixed, bScientific, bHex), TEXT("Illegal conversion. Please check the IsFloatingPoint()."));
return Result;
return TStringView(*this).ToFloatAndTrim<U>(bFixed, bScientific);
}
/** Parse the string into a boolean value. */
NODISCARD FORCEINLINE constexpr bool Parse(bool& Value)
{
return Algorithms::Parse(*this, Value);
}
/** Converts a string into a boolean value and remove the parsed substring. */
NODISCARD constexpr bool ToBoolAndTrim();
/** Parse the string into an integer value. */
/** Converts a string into an integer value and remove the parsed substring. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, uint Base = 0)
NODISCARD constexpr U ToIntAndTrim(unsigned Base = 10);
/** Converts a string into a floating-point value and remove the parsed substring. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD constexpr U ToFloatAndTrim(bool bFixed = true, bool bScientific = true);
public:
/**
* Parse a string using a format string to objects.
*
* @param Fmt - The format string.
* @param Args - The objects to parse.
*
* @return The number of objects successfully parsed.
*/
template <typename... Ts>
size_t Parse(TStringView Fmt, Ts&... Args) const
{
return Algorithms::Parse(*this, Value, Base);
return TStringView(*this).ParseAndTrim(Fmt, Args...);
}
/** Parse the string into a floating-point value. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, bool bFixed = true, bool bScientific = true, bool bHex = true)
{
return Algorithms::Parse(*this, Value, bFixed, bScientific, bHex);
}
/** Parse a string using a format string to objects and remove the parsed substring. */
template <typename... Ts>
size_t ParseAndTrim(TStringView Fmt, Ts&... Args);
public:

View File

@ -278,37 +278,28 @@ struct TTTupleSynthThreeWayComparable<TTypeSequence<>, TTypeSequence<>> : FTrue
template <typename TSequence, typename USequence>
concept CTTupleSynthThreeWayComparable = TTTupleSynthThreeWayComparable<TSequence, USequence>::Value;
template <typename Ret, typename F, typename TTupleType>
struct TTupleVisitElementByIndex
template <typename Ret, typename Indices>
struct TTupleVisitElementByIndex;
template <typename Ret, size_t I, size_t... Indices>
struct TTupleVisitElementByIndex<Ret, TIndexSequence<I, Indices...>>
{
template <size_t Index>
struct TInvokeElement
template <typename F, typename TTupleType>
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, TTupleType&& Arg, size_t Index)
{
FORCEINLINE static constexpr Ret Do(F&& Func, TTupleType&& Arg)
{
return InvokeResult<Ret>(Forward<F>(Func), Forward<TTupleType>(Arg).template GetValue<Index>());
if (Index == I) return InvokeResult<Ret>(Forward<F>(Func), Forward<TTupleType>(Arg).template GetValue<I>());
return TTupleVisitElementByIndex<Ret, TIndexSequence<Indices...>>::Do(Forward<F>(Func), Forward<TTupleType>(Arg), Index);
}
};
};
template <typename>
struct TInvokeTuple;
template <size_t... Indices>
struct TInvokeTuple<TIndexSequence<Indices...>>
template <typename Ret>
struct TTupleVisitElementByIndex<Ret, TIndexSequence<>>
{
template <typename F, typename TTupleType>
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, TTupleType&& Arg, size_t)
{
FORCEINLINE static constexpr Ret Do(F&& Func, TTupleType&& Arg, size_t Index)
{
using FInvokeImplType = Ret(*)(F&&, TTupleType&&);
constexpr FInvokeImplType InvokeImpl[] = { TInvokeElement<Indices>::Do... };
return InvokeImpl[Index](Forward<F>(Func), Forward<TTupleType>(Arg));
}
};
FORCEINLINE static constexpr Ret Do(F&& Func, TTupleType&& Arg, size_t Index)
{
return TInvokeTuple<TMakeIndexSequence<Arg.Num()>>::Do(Forward<F>(Func), Forward<TTupleType>(Arg), Index);
checkf(false, "Read access violation. Please check Index.");
return InvokeResult<Ret>(Forward<F>(Func), Forward<TTupleType>(Arg).template GetValue<0>());
}
};
@ -430,64 +421,64 @@ public:
template <typename T> NODISCARD FORCEINLINE constexpr decltype(auto) GetValue() const volatile&& { return static_cast<const volatile TTuple&&>(*this).GetValue<TTupleIndex<T, TTuple>>(); }
/** 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 FHelper::Apply(Forward<F>(Func), static_cast< TTuple& >(*this)); }
template <typename F> requires (CInvocable<F&&, const Ts& ...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const & { return FHelper::Apply(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
template <typename F> requires (CInvocable<F&&, volatile Ts& ...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile& { return FHelper::Apply(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
template <typename F> requires (CInvocable<F&&, const volatile Ts& ...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile& { return FHelper::Apply(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
template <typename F> requires (CInvocable<F&&, Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) && { return FHelper::Apply(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
template <typename F> requires (CInvocable<F&&, const Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const && { return FHelper::Apply(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
template <typename F> requires (CInvocable<F&&, volatile Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) volatile&& { return FHelper::Apply(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
template <typename F> requires (CInvocable<F&&, const volatile Ts&&...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const volatile&& { return FHelper::Apply(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) & { return FHelper::Apply(Forward<F>(Func), static_cast< TTuple& >(*this)); }
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const & { return FHelper::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 FHelper::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 FHelper::Apply(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) && { return FHelper::Apply(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
template <typename F> requires (CInvocable<F, Ts...>) FORCEINLINE constexpr decltype(auto) Apply(F&& Func) const && { return FHelper::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 FHelper::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 FHelper::Apply(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
/** Visits each element in a tuple in parallel and applies it as arguments to the function. */
template <typename F> requires (true && ... && CInvocable<F&&, Ts& >) FORCEINLINE constexpr void Visit(F&& Func) & { VisitTuple(Forward<F>(Func), static_cast< TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F&&, const Ts& >) FORCEINLINE constexpr void Visit(F&& Func) const & { VisitTuple(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F&&, volatile Ts& >) FORCEINLINE constexpr void Visit(F&& Func) volatile& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F&&, const volatile Ts& >) FORCEINLINE constexpr void Visit(F&& Func) const volatile& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F&&, Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) && { VisitTuple(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
template <typename F> requires (true && ... && CInvocable<F&&, const Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) const && { VisitTuple(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
template <typename F> requires (true && ... && CInvocable<F&&, volatile Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) volatile&& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
template <typename F> requires (true && ... && CInvocable<F&&, const volatile Ts&&>) FORCEINLINE constexpr void Visit(F&& Func) const volatile&& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) & { VisitTuple(Forward<F>(Func), static_cast< TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const & { VisitTuple(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) volatile& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const volatile& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple& >(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) && { VisitTuple(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const && { VisitTuple(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) volatile&& { VisitTuple(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
template <typename F> requires (true && ... && CInvocable<F, Ts>) FORCEINLINE constexpr void Visit(F&& Func) const volatile&& { VisitTuple(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this)); }
/** Visits specified element in a tuple and applies it as arguments to the function. */
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, Ts& >) && CCommonReference<TInvokeResult<F&&, Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) & { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, Ts& >...>>(Forward<F>(Func), Index); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const Ts& >) && CCommonReference<TInvokeResult<F&&, const Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const & { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, const Ts& >...>>(Forward<F>(Func), Index); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, volatile Ts& >) && CCommonReference<TInvokeResult<F&&, volatile Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< volatile TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, volatile Ts& >...>>(Forward<F>(Func), Index); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const volatile Ts& >) && CCommonReference<TInvokeResult<F&&, const volatile Ts& >...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const volatile TTuple& >(*this).Visit<TCommonReference<TInvokeResult<F&&, const volatile Ts& >...>>(Forward<F>(Func), Index); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, Ts&&>) && CCommonReference<TInvokeResult<F&&, Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) && { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, Ts&&>...>>(Forward<F>(Func), Index); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const Ts&&>) && CCommonReference<TInvokeResult<F&&, const Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const && { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, const Ts&&>...>>(Forward<F>(Func), Index); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, volatile Ts&&>) && CCommonReference<TInvokeResult<F&&, volatile Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast< volatile TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, volatile Ts&&>...>>(Forward<F>(Func), Index); }
template <typename F> requires (((sizeof...(Ts) >= 1) && ... && CInvocable<F&&, const volatile Ts&&>) && CCommonReference<TInvokeResult<F&&, const volatile Ts&&>...>) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return static_cast<const volatile TTuple&&>(*this).Visit<TCommonReference<TInvokeResult<F&&, const volatile Ts&&>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) & { return static_cast< TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const & { return static_cast<const TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile& { return static_cast< volatile TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile& { return static_cast<const volatile TTuple& >(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) && { return static_cast< TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const && { return static_cast<const TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) volatile&& { return static_cast< volatile TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
template <typename F> requires ((sizeof...(Ts) >= 1 && CCommonType<TInvokeResult<F, Ts>...>) && ... && (CInvocable<F, Ts>)) FORCEINLINE constexpr decltype(auto) Visit(F&& Func, size_t Index) const volatile&& { return static_cast<const volatile TTuple&&>(*this).Visit<TCommonType<TInvokeResult<F, Ts>...>>(Forward<F>(Func), Index); }
/** Visits specified element in a tuple and applies it as arguments to the function. */
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) & { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, TTuple& >::Do(Forward<F>(Func), static_cast< TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const & { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const TTuple& >::Do(Forward<F>(Func), static_cast<const TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, volatile Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, volatile TTuple& >::Do(Forward<F>(Func), static_cast< volatile TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const volatile Ts& >) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const volatile TTuple& >::Do(Forward<F>(Func), static_cast<const volatile TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) && { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, TTuple&&>::Do(Forward<F>(Func), static_cast< TTuple&&>(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const && { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const TTuple&&>::Do(Forward<F>(Func), static_cast<const TTuple&&>(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, volatile Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, volatile TTuple&&>::Do(Forward<F>(Func), static_cast< volatile TTuple&&>(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F&&, const volatile Ts&&>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile&& { checkf(Index < Num(), "Read access violation. Please check Index."); return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, F, const volatile TTuple&&>::Do(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) & { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const & { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< volatile TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const volatile TTuple& >(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) && { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< TTuple&&>(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const && { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const TTuple&&>(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) volatile&& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast< volatile TTuple&&>(*this), Index); }
template <typename Ret, typename F> requires ((sizeof...(Ts) >= 1) && ... && CInvocableResult<Ret, F, Ts>) FORCEINLINE constexpr Ret Visit(F&& Func, size_t Index) const volatile&& { return NAMESPACE_PRIVATE::TTupleVisitElementByIndex<Ret, TMakeIndexSequence<sizeof...(Ts)>>::Do(Forward<F>(Func), static_cast<const volatile TTuple&&>(*this), Index); }
/** 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 FHelper::Transform(Forward<F>(Func), static_cast< TTuple& >(*this)); }
template <typename F> requires (true && ... && (CInvocable<F&&, const Ts& > && !CSameAs<void, TInvokeResult<F&&, const Ts& >>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const & { return FHelper::Transform(Forward<F>(Func), static_cast<const TTuple& >(*this)); }
template <typename F> requires (true && ... && (CInvocable<F&&, volatile Ts& > && !CSameAs<void, TInvokeResult<F&&, volatile Ts& >>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile& { return FHelper::Transform(Forward<F>(Func), static_cast< volatile TTuple& >(*this)); }
template <typename F> requires (true && ... && (CInvocable<F&&, const volatile Ts& > && !CSameAs<void, TInvokeResult<F&&, const volatile Ts& >>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile& { return FHelper::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 FHelper::Transform(Forward<F>(Func), static_cast< TTuple&&>(*this)); }
template <typename F> requires (true && ... && (CInvocable<F&&, const Ts&&> && !CSameAs<void, TInvokeResult<F&&, const Ts&&>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const && { return FHelper::Transform(Forward<F>(Func), static_cast<const TTuple&&>(*this)); }
template <typename F> requires (true && ... && (CInvocable<F&&, volatile Ts&&> && !CSameAs<void, TInvokeResult<F&&, volatile Ts&&>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) volatile&& { return FHelper::Transform(Forward<F>(Func), static_cast< volatile TTuple&&>(*this)); }
template <typename F> requires (true && ... && (CInvocable<F&&, const volatile Ts&&> && !CSameAs<void, TInvokeResult<F&&, const volatile Ts&&>>)) NODISCARD FORCEINLINE constexpr decltype(auto) Transform(F&& Func) const volatile&& { return FHelper::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 FHelper::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 FHelper::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 FHelper::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 FHelper::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 FHelper::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 FHelper::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 FHelper::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 FHelper::Transform(Forward<F>(Func), 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 FHelper::template Construct<T>(static_cast< TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, const Ts& ...>) NODISCARD FORCEINLINE constexpr T Construct() const & { return FHelper::template Construct<T>(static_cast<const TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, volatile Ts& ...>) NODISCARD FORCEINLINE constexpr T Construct() volatile& { return FHelper::template Construct<T>(static_cast< volatile TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, const volatile Ts& ...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile& { return FHelper::template Construct<T>(static_cast<const volatile TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() && { return FHelper::template Construct<T>(static_cast< TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, const Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() const && { return FHelper::template Construct<T>(static_cast<const TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, volatile Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() volatile&& { return FHelper::template Construct<T>(static_cast< volatile TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, const volatile Ts&&...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile&& { return FHelper::template Construct<T>(static_cast<const volatile TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() & { return FHelper::template Construct<T>(static_cast< TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const & { return FHelper::template Construct<T>(static_cast<const TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile& { return FHelper::template Construct<T>(static_cast< volatile TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile& { return FHelper::template Construct<T>(static_cast<const volatile TTuple& >(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() && { return FHelper::template Construct<T>(static_cast< TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const && { return FHelper::template Construct<T>(static_cast<const TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() volatile&& { return FHelper::template Construct<T>(static_cast< volatile TTuple&&>(*this)); }
template <typename T> requires (CConstructibleFrom<T, Ts...>) NODISCARD FORCEINLINE constexpr T Construct() const volatile&& { return FHelper::template Construct<T>(static_cast<const volatile TTuple&&>(*this)); }
/** @return The number of elements in the tuple. */
NODISCARD static FORCEINLINE constexpr size_t Num() { return sizeof...(Ts); }

View File

@ -433,11 +433,11 @@ private:
using FMoveAssignImpl = void(*)(void*, void*);
using FDestroyImpl = void(*)(void* );
static constexpr FCopyConstructImpl CopyConstructImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyConstruct (A, B); }) Memory::CopyConstruct (static_cast<Ts*>(A), static_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy constructible."), typeid(Ts).name()); }... };
static constexpr FMoveConstructImpl MoveConstructImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveConstruct (A, B); }) Memory::MoveConstruct (static_cast<Ts*>(A), static_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move constructible."), typeid(Ts).name()); }... };
static constexpr FCopyAssignImpl CopyAssignImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyAssign (A, B); }) Memory::CopyAssign (static_cast<Ts*>(A), static_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy assignable."), typeid(Ts).name()); }... };
static constexpr FMoveAssignImpl MoveAssignImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveAssign (A, B); }) Memory::MoveAssign (static_cast<Ts*>(A), static_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move assignable."), typeid(Ts).name()); }... };
static constexpr FDestroyImpl DestroyImpl[] = { [](void* A ) { if constexpr (requires(Ts* A ) { Memory::Destruct (A ); }) Memory::Destruct (static_cast<Ts*>(A) ); else checkf(false, TEXT("The type '%s' is not destructible."), typeid(Ts).name()); }... };
static constexpr FCopyConstructImpl CopyConstructImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyConstruct (A, B); }) Memory::CopyConstruct (reinterpret_cast<Ts*>(A), reinterpret_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy constructible."), typeid(Ts).name()); }... };
static constexpr FMoveConstructImpl MoveConstructImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveConstruct (A, B); }) Memory::MoveConstruct (reinterpret_cast<Ts*>(A), reinterpret_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move constructible."), typeid(Ts).name()); }... };
static constexpr FCopyAssignImpl CopyAssignImpl[] = { [](void* A, const void* B) { if constexpr (requires(Ts* A, const Ts* B) { Memory::CopyAssign (A, B); }) Memory::CopyAssign (reinterpret_cast<Ts*>(A), reinterpret_cast<const Ts*>(B)); else checkf(false, TEXT("The type '%s' is not copy assignable."), typeid(Ts).name()); }... };
static constexpr FMoveAssignImpl MoveAssignImpl[] = { [](void* A, void* B) { if constexpr (requires(Ts* A, Ts* B) { Memory::MoveAssign (A, B); }) Memory::MoveAssign (reinterpret_cast<Ts*>(A), reinterpret_cast< Ts*>(B)); else checkf(false, TEXT("The type '%s' is not move assignable."), typeid(Ts).name()); }... };
static constexpr FDestroyImpl DestroyImpl[] = { [](void* A ) { if constexpr (requires(Ts* A ) { Memory::Destruct (A ); }) Memory::Destruct (reinterpret_cast<Ts*>(A) ); else checkf(false, TEXT("The type '%s' is not destructible."), typeid(Ts).name()); }... };
TAlignedUnion<1, Ts...> Value;
uint8 TypeIndex;
@ -502,10 +502,10 @@ struct TVariantVisitImpl
};
template <size_t EncodedIndex, typename>
struct TInvokeEncoded;
struct FInvokeEncoded;
template <size_t EncodedIndex, size_t... ExtentIndices>
struct TInvokeEncoded<EncodedIndex, TIndexSequence<ExtentIndices...>>
struct FInvokeEncoded<EncodedIndex, TIndexSequence<ExtentIndices...>>
{
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
{
@ -513,7 +513,7 @@ struct TVariantVisitImpl
}
template <typename Ret>
struct TResult
struct FResult
{
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
{
@ -523,26 +523,26 @@ struct TVariantVisitImpl
};
template <typename>
struct TInvokeVariant;
struct FInvokeVariant;
template <size_t... EncodedIndices>
struct TInvokeVariant<TIndexSequence<EncodedIndices...>>
struct FInvokeVariant<TIndexSequence<EncodedIndices...>>
{
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
{
using FExtentIndices = TIndexSequenceFor<VariantTypes...>;
using FResultType = TCommonReference<decltype(TInvokeEncoded<EncodedIndices, FExtentIndices>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...))...>;
using FResultType = TCommonType<decltype(FInvokeEncoded<EncodedIndices, FExtentIndices>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...))...>;
using FInvokeImplType = FResultType(*)(F&&, VariantTypes&&...);
constexpr FInvokeImplType InvokeImpl[] = { TInvokeEncoded<EncodedIndices, FExtentIndices>::template TResult<FResultType>::Do... };
constexpr FInvokeImplType InvokeImpl[] = { FInvokeEncoded<EncodedIndices, FExtentIndices>::template FResult<FResultType>::Do... };
return InvokeImpl[FEncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
template <typename Ret>
struct TResult
struct FResult
{
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
{
@ -550,7 +550,7 @@ struct TVariantVisitImpl
using FInvokeImplType = Ret(*)(F&&, VariantTypes&&...);
constexpr FInvokeImplType InvokeImpl[] = { TInvokeEncoded<EncodedIndices, FExtentIndices>::template TResult<Ret>::Do... };
constexpr FInvokeImplType InvokeImpl[] = { FInvokeEncoded<EncodedIndices, FExtentIndices>::template FResult<Ret>::Do... };
return InvokeImpl[FEncodeIndices::Do({ Variants.GetIndex()... })](Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
@ -559,15 +559,15 @@ struct TVariantVisitImpl
FORCEINLINE static constexpr decltype(auto) Do(F&& Func, VariantTypes&&... Variants)
{
return TInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
return FInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
template <typename Ret>
struct TResult
struct FResult
{
FORCEINLINE static constexpr Ret Do(F&& Func, VariantTypes&&... Variants)
{
return TInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::template TResult<Ret>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
return FInvokeVariant<TMakeIndexSequence<FGetTotalNum::Do()>>::template FResult<Ret>::Do(Forward<F>(Func), Forward<VariantTypes>(Variants)...);
}
};
};
@ -589,7 +589,7 @@ template <typename Ret, typename F, typename FirstVariantType, typename... Varia
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<F, FirstVariantType, VariantTypes...>::template TResult<Ret>::Do(Forward<F>(Func), Forward<FirstVariantType>(FirstVariant), Forward<VariantTypes>(Variants)...);
return NAMESPACE_PRIVATE::TVariantVisitImpl<F, FirstVariantType, VariantTypes...>::template FResult<Ret>::Do(Forward<F>(Func), Forward<FirstVariantType>(FirstVariant), Forward<VariantTypes>(Variants)...);
}
NAMESPACE_MODULE_END(Utility)