Compare commits
No commits in common. "9dd5e747884da3cce7242899823bc0c4a9a37e96" and "bf2239712332ac27b74d44dfb4900788fe2e1592" have entirely different histories.
9dd5e74788
...
bf22397123
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
);
|
||||
|
@ -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 = { })
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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) { }
|
||||
|
@ -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
@ -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)
|
@ -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
|
@ -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. */
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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); }
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user