Compare commits

...

3 Commits

5 changed files with 698 additions and 109 deletions

View File

@ -77,13 +77,13 @@ void TestChar()
always_check(TChar<T>::ToLower(LITERAL(T, 'i')) == LITERAL(T, 'i')); always_check(TChar<T>::ToLower(LITERAL(T, 'i')) == LITERAL(T, 'i'));
always_check(TChar<T>::ToUpper(LITERAL(T, 'l')) == LITERAL(T, 'L')); always_check(TChar<T>::ToUpper(LITERAL(T, 'l')) == LITERAL(T, 'L'));
always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0'))); always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0'), 16));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f'))); always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f'), 16));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F'))); always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F'), 16));
always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0)); always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0, 16));
always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF)); always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF, 16));
always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF)); always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF, 16));
}; };
Test(InPlaceType<char>); Test(InPlaceType<char>);
@ -536,6 +536,70 @@ void TestStringConversion()
CheckParseFloat(InPlaceType<float>); CheckParseFloat(InPlaceType<float>);
CheckParseFloat(InPlaceType<double>); CheckParseFloat(InPlaceType<double>);
CheckParseFloat(InPlaceType<long double>); CheckParseFloat(InPlaceType<long double>);
{
always_check(TString<T>::FromBool(true ) == LITERAL(T, "True" ));
always_check(TString<T>::FromBool(false) == LITERAL(T, "False"));
}
{
always_check(TString<T>::FromInt(42) == LITERAL(T, "42" ));
always_check(TString<T>::FromInt(255, 16) == LITERAL(T, "FF" ));
always_check(TString<T>::FromInt(-42) == LITERAL(T, "-42" ));
always_check(TString<T>::FromInt(0) == LITERAL(T, "0" ));
always_check(TString<T>::FromInt(1234567890) == LITERAL(T, "1234567890"));
always_check(TString<T>::FromInt(255, 2) == LITERAL(T, "11111111" ));
always_check(TString<T>::FromInt(255, 8) == LITERAL(T, "377" ));
always_check(TString<T>::FromInt(255, 36) == LITERAL(T, "73" ));
}
{
always_check(TString<T>::FromFloat(3.14f) == LITERAL(T, "3.14" ));
always_check(TString<T>::FromFloat(0.0f) == LITERAL(T, "0" ));
always_check(TString<T>::FromFloat(-3.14f) == LITERAL(T, "-3.14" ));
always_check(TString<T>::FromFloat(3.14f, true, false) == LITERAL(T, "3.14" ));
always_check(TString<T>::FromFloat(3.14f, false, true) == LITERAL(T, "3.14e+00"));
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() == NAMESPACE_STD::numeric_limits<int>::max());
always_check(LITERAL_VIEW(T, "-999999999999999999999999999999").ToInt() == NAMESPACE_STD::numeric_limits<int>::min());
}
{
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(LITERAL_VIEW(T, "1e+308").ToFloat() == NAMESPACE_STD::numeric_limits<float>::infinity());
always_check(LITERAL_VIEW(T, "-1e+308").ToFloat() == -NAMESPACE_STD::numeric_limits<float>::infinity());
always_check(LITERAL_VIEW(T, "1e-308").ToFloat() == 0.0f);
always_check(LITERAL_VIEW(T, "-1e-308").ToFloat() == -0.0f);
always_check(LITERAL_VIEW(T, "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.e-4").ToFloat() == NAMESPACE_STD::numeric_limits<float>::infinity());
always_check(LITERAL_VIEW(T, "-100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.e-4").ToFloat() == -NAMESPACE_STD::numeric_limits<float>::infinity());
always_check(LITERAL_VIEW(T, "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-4").ToFloat() == NAMESPACE_STD::numeric_limits<float>::infinity());
always_check(LITERAL_VIEW(T, "-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-4").ToFloat() == -NAMESPACE_STD::numeric_limits<float>::infinity());
always_check(LITERAL_VIEW(T, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e+4").ToFloat() == 0.0f);
always_check(LITERAL_VIEW(T, "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e+4").ToFloat() == -0.0f);
}
}; };
Test(InPlaceType<char>); Test(InPlaceType<char>);

View File

@ -161,7 +161,7 @@ struct TChar
return false; return false;
} }
NODISCARD FORCEINLINE static constexpr bool IsASCII(CharType InChar) NODISCARD FORCEINLINE static constexpr bool IsASCII(CharType InChar = LITERAL(CharType, '\0'))
{ {
if constexpr (CSameAs<CharType, char>) if constexpr (CSameAs<CharType, char>)
{ {
@ -359,19 +359,30 @@ struct TChar
NODISCARD FORCEINLINE static constexpr bool IsDigit(CharType InChar) NODISCARD FORCEINLINE static constexpr bool IsDigit(CharType InChar)
{ {
static_assert(TChar::IsASCII());
/* <U0030>..<U0039>; */ /* <U0030>..<U0039>; */
return (InChar >= LITERAL(CharType, '0') && InChar <= LITERAL(CharType, '9')); return InChar >= LITERAL(CharType, '0') && InChar <= LITERAL(CharType, '9');
} }
NODISCARD FORCEINLINE static constexpr bool IsDigit(CharType InChar, unsigned Base) NODISCARD FORCEINLINE static constexpr bool IsDigit(CharType InChar, unsigned Base)
{ {
checkf(Base >= 2 && Base <= 36, TEXT("Base must be in the range [2, 36].")); checkf(Base >= 2 && Base <= 36, TEXT("Base must be in the range [2, 36]."));
/* <U0030>..<U0039>;<U0041>..<U0046>;<U0061>..<U0066>; */ static_assert(TChar::IsASCII());
return
(InChar >= LITERAL(CharType, '0') && InChar < LITERAL(CharType, '0') + static_cast<signed>(Base) ) || bool bResult = false;
(InChar >= LITERAL(CharType, 'a') && InChar < LITERAL(CharType, 'a') + static_cast<signed>(Base) - 10) ||
(InChar >= LITERAL(CharType, 'A') && InChar < LITERAL(CharType, 'A') + static_cast<signed>(Base) - 10); /* <U0030>..<U0039>; */
bResult |= InChar >= LITERAL(CharType, '0') && InChar < LITERAL(CharType, '0') + static_cast<signed>(Base);
/* <U0041>..<U0046>; */
bResult |= InChar >= LITERAL(CharType, 'a') && InChar < LITERAL(CharType, 'a') + static_cast<signed>(Base) - 10;
/* <U0061>..<U0066>; */
bResult |= InChar >= LITERAL(CharType, 'A') && InChar < LITERAL(CharType, 'A') + static_cast<signed>(Base) - 10;
return bResult;
} }
NODISCARD FORCEINLINE static constexpr bool IsCntrl(CharType InChar) NODISCARD FORCEINLINE static constexpr bool IsCntrl(CharType InChar)
@ -798,79 +809,42 @@ struct TChar
return InChar; return InChar;
} }
NODISCARD FORCEINLINE static constexpr TOptional<unsigned> ToDigit(CharType InChar) NODISCARD FORCEINLINE static constexpr TOptional<unsigned> ToDigit(CharType InChar, unsigned Base = 10)
{ {
switch (InChar) static_assert(TChar::IsASCII());
constexpr uint8 DigitFromChar[] =
{ {
case LITERAL(CharType, '0'): return 0; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, '1'): return 1; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, '2'): return 2; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, '3'): return 3; 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, '4'): return 4; 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
case LITERAL(CharType, '5'): return 5; 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, '6'): return 6; 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
case LITERAL(CharType, '7'): return 7; 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, '8'): return 8; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, '9'): return 9; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, 'a'): return 10; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, 'b'): return 11; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, 'c'): return 12; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, 'd'): return 13; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, 'e'): return 14; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, 'f'): return 15; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
case LITERAL(CharType, 'g'): return 16; };
case LITERAL(CharType, 'h'): return 17;
case LITERAL(CharType, 'i'): return 18; static_assert(sizeof(DigitFromChar) == 256);
case LITERAL(CharType, 'j'): return 19;
case LITERAL(CharType, 'k'): return 20; if constexpr (sizeof(CharType) > 1) if (InChar >> 8) return Invalid;
case LITERAL(CharType, 'l'): return 21;
case LITERAL(CharType, 'm'): return 22; if (DigitFromChar[InChar] >= Base) return Invalid;
case LITERAL(CharType, 'n'): return 23;
case LITERAL(CharType, 'o'): return 24; return DigitFromChar[InChar];
case LITERAL(CharType, 'p'): return 25;
case LITERAL(CharType, 'q'): return 26;
case LITERAL(CharType, 'r'): return 27;
case LITERAL(CharType, 's'): return 28;
case LITERAL(CharType, 't'): return 29;
case LITERAL(CharType, 'u'): return 30;
case LITERAL(CharType, 'v'): return 31;
case LITERAL(CharType, 'w'): return 32;
case LITERAL(CharType, 'x'): return 33;
case LITERAL(CharType, 'y'): return 34;
case LITERAL(CharType, 'z'): return 35;
case LITERAL(CharType, 'A'): return 10;
case LITERAL(CharType, 'B'): return 11;
case LITERAL(CharType, 'C'): return 12;
case LITERAL(CharType, 'D'): return 13;
case LITERAL(CharType, 'E'): return 14;
case LITERAL(CharType, 'F'): return 15;
case LITERAL(CharType, 'G'): return 16;
case LITERAL(CharType, 'H'): return 17;
case LITERAL(CharType, 'I'): return 18;
case LITERAL(CharType, 'J'): return 19;
case LITERAL(CharType, 'K'): return 20;
case LITERAL(CharType, 'L'): return 21;
case LITERAL(CharType, 'M'): return 22;
case LITERAL(CharType, 'N'): return 23;
case LITERAL(CharType, 'O'): return 24;
case LITERAL(CharType, 'P'): return 25;
case LITERAL(CharType, 'Q'): return 26;
case LITERAL(CharType, 'R'): return 27;
case LITERAL(CharType, 'S'): return 28;
case LITERAL(CharType, 'T'): return 29;
case LITERAL(CharType, 'U'): return 30;
case LITERAL(CharType, 'V'): return 31;
case LITERAL(CharType, 'W'): return 32;
case LITERAL(CharType, 'X'): return 33;
case LITERAL(CharType, 'Y'): return 34;
case LITERAL(CharType, 'Z'): return 35;
default: return Invalid;
}
} }
NODISCARD FORCEINLINE static constexpr TOptional<CharType> FromDigit(int InDigit) NODISCARD FORCEINLINE static constexpr TOptional<CharType> FromDigit(unsigned InDigit, unsigned Base = 10)
{ {
if (InDigit < 0 || InDigit > 35) return Invalid; if (InDigit > Base) return Invalid;
return LITERAL(CharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit]; return LITERAL(CharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit];
} }

View File

@ -8,9 +8,10 @@
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <charconv>
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4146) #pragma warning(disable : 4146 4244)
NAMESPACE_REDCRAFT_BEGIN NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Redcraft)
@ -33,7 +34,7 @@ NAMESPACE_PRIVATE_BEGIN
template <CCharType T, bool bIsFormat> template <CCharType T, bool bIsFormat>
struct TStringHelper struct TStringHelper
{ {
FORCEINLINE static bool FormatObject(auto& Result, TStringView<T> Fmt, auto& Object) requires (bIsFormat) static FORCEINLINE bool FormatObject(auto& Result, TStringView<T> Fmt, auto& Object) requires (bIsFormat)
{ {
using U = TRemoveCVRef<decltype(Object)>; using U = TRemoveCVRef<decltype(Object)>;
@ -119,7 +120,7 @@ struct TStringHelper
return false; return false;
} }
FORCEINLINE static bool ParseObject(TStringView<T>& View, TStringView<T> Fmt, auto& Object) requires (!bIsFormat) static FORCEINLINE bool ParseObject(TStringView<T>& View, TStringView<T> Fmt, auto& Object) requires (!bIsFormat)
{ {
using U = TRemoveCVRef<decltype(Object)>; using U = TRemoveCVRef<decltype(Object)>;
@ -209,9 +210,13 @@ struct TStringHelper
{ {
NumberType Result = Init; NumberType Result = Init;
while (!View.IsEmpty() && (Base == 10 ? TChar<T>::IsDigit(View.Front()) : TChar<T>::IsDigit(View.Front(), Base))) while (!View.IsEmpty())
{ {
Result = static_cast<NumberType>(Result * Base + *TChar<T>::ToDigit(View.Front())); auto Digit = TChar<T>::ToDigit(View.Front(), Base);
if (!Digit) break;
Result = Result * static_cast<NumberType>(Base) + static_cast<NumberType>(*Digit);
View.RemovePrefix(1); View.RemovePrefix(1);
} }
@ -324,7 +329,7 @@ struct TStringHelper
return false; return false;
} }
FORCEINLINE static size_t Do(auto& Result, TStringView<T> Fmt, auto ArgsTuple) static FORCEINLINE size_t Do(auto& Result, TStringView<T> Fmt, auto ArgsTuple)
{ {
size_t FormattedObjectNum = 0; size_t FormattedObjectNum = 0;
@ -574,6 +579,355 @@ size_t TStringView<T>::Parse(TStringView Fmt, Ts&... Args) const
#undef ESCAPE_LEFT_BRACE #undef ESCAPE_LEFT_BRACE
#undef ESCAPE_RIGHT_BRACE #undef ESCAPE_RIGHT_BRACE
template <CCharType T>
constexpr bool TStringView<T>::ToBool() const
{
if (StartsWith(LITERAL(T, '1'))
|| StartsWith(LITERAL(T, "true"))
|| StartsWith(LITERAL(T, "True"))
|| StartsWith(LITERAL(T, "TRUE")))
{
return true;
}
if (StartsWith(LITERAL(T, '0'))
|| StartsWith(LITERAL(T, "false"))
|| StartsWith(LITERAL(T, "False"))
|| StartsWith(LITERAL(T, "FALSE")))
{
return false;
}
return ToInt() != 0;
}
template <CCharType T>
template <CIntegral U> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
constexpr U TStringView<T>::ToInt(unsigned Base) const
{
checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base."));
bool bNegative = false;
TStringView View = *this;
if constexpr (CSigned<U>)
{
if (View.StartsWith(LITERAL(ElementType, '-')))
{
bNegative = true;
View.RemovePrefix(1);
}
}
using UnsignedU = TMakeUnsigned<U>;
constexpr UnsignedU UnsignedMaximum = static_cast<UnsignedU>(-1);
constexpr U SignedMaximum = static_cast<U>(UnsignedMaximum >> 1);
constexpr U SignedMinimum = -static_cast<U>(SignedMaximum) - 1;
UnsignedU LastValue = 0;
UnsignedU Value = 0;
for (ElementType Char : View)
{
auto Digit = TChar<ElementType>::ToDigit(Char, Base);
if (!Digit) break;
LastValue = Value;
Value = static_cast<UnsignedU>(LastValue * Base + *Digit);
if (Value < LastValue)
{
if constexpr (CSigned<U>)
{
return bNegative ? SignedMinimum : SignedMaximum;
}
else return UnsignedMaximum;
}
}
if constexpr (CSigned<U>)
{
if (!bNegative && Value >= static_cast<UnsignedU>(SignedMaximum)) return SignedMaximum;
if ( bNegative && Value >= static_cast<UnsignedU>(SignedMinimum)) return SignedMinimum;
if (bNegative) Value = static_cast<UnsignedU>(-Value);
}
return static_cast<U>(Value);
}
template <CCharType T>
template <CFloatingPoint U> requires (!CConst<U> && !CVolatile<U>)
constexpr U TStringView<T>::ToFloat(bool bFixed, bool bScientific) const
{
NAMESPACE_STD::chars_format Format;
if ( bFixed && bScientific) Format = NAMESPACE_STD::chars_format::general;
else if ( bFixed && !bScientific) Format = NAMESPACE_STD::chars_format::fixed;
else if (!bFixed && bScientific) Format = NAMESPACE_STD::chars_format::scientific;
else Format = NAMESPACE_STD::chars_format::hex;
U Result;
auto Iter = this->Begin();
bool bNegativeMantissa = false;
bool bNegativeExponent = false;
do
{
if (Iter == this->End()) break;
if (*Iter == LITERAL(ElementType, '-'))
{
bNegativeMantissa = true;
++Iter;
}
auto DecimalPoint = this->End();
auto NonZeroBegin = this->End();
while (Iter != this->End())
{
if (DecimalPoint == this->End() && *Iter == LITERAL(ElementType, '.'))
{
DecimalPoint = Iter;
}
else if (TChar<ElementType>::IsDigit(*Iter, Format == NAMESPACE_STD::chars_format::hex ? 16 : 10))
{
if (NonZeroBegin == this->End() && *Iter != LITERAL(ElementType, '0'))
{
NonZeroBegin = Iter;
}
}
else break;
++Iter;
}
if (DecimalPoint == this->End()) DecimalPoint = Iter;
bNegativeExponent = DecimalPoint < NonZeroBegin;
if (Iter == this->End()) break;
bool bHasExponent = false;
if (Format == NAMESPACE_STD::chars_format::general || Format == NAMESPACE_STD::chars_format::scientific)
{
if (*Iter == LITERAL(ElementType, 'e') || *Iter == LITERAL(ElementType, 'E'))
{
bHasExponent = true;
++Iter;
}
}
else if (Format == NAMESPACE_STD::chars_format::hex)
{
if (*Iter == LITERAL(ElementType, 'p') || *Iter == LITERAL(ElementType, 'P'))
{
bHasExponent = true;
++Iter;
}
}
if (Iter == this->End() || !bHasExponent) break;
if (*Iter == LITERAL(ElementType, '+')) ++Iter;
if (*Iter == LITERAL(ElementType, '-')) { bNegativeExponent = true; ++Iter; }
auto ExponentBegin = Iter;
while (Iter != this->End() && TChar<ElementType>::IsDigit(*Iter, 10)) ++Iter;
auto ExponentEnd = Iter;
if (NonZeroBegin == this->End()) break;
auto Exponent = TStringView(ExponentBegin, ExponentEnd).ToInt();
if (bNegativeExponent) Exponent = -Exponent;
Exponent += static_cast<int>(DecimalPoint - NonZeroBegin);
bNegativeExponent = Exponent < 0;
}
while (false);
NAMESPACE_STD::from_chars_result ConvertResult;
if constexpr (!CSameAs<ElementType, char>)
{
TArray<char, TInlineAllocator<64>> Buffer(this->Begin(), Iter);
ConvertResult = NAMESPACE_STD::from_chars(ToAddress(Buffer.Begin()), ToAddress(Buffer.End()), Result, Format);
}
else ConvertResult = NAMESPACE_STD::from_chars(ToAddress(this->Begin()), ToAddress(this->End()), Result, Format);
if (ConvertResult.ec == NAMESPACE_STD::errc::result_out_of_range)
{
if (!bNegativeMantissa && !bNegativeExponent) return NAMESPACE_STD::numeric_limits<U>::infinity();
if ( bNegativeMantissa && !bNegativeExponent) return -NAMESPACE_STD::numeric_limits<U>::infinity();
if (!bNegativeMantissa && bNegativeExponent) return static_cast<U>( 0.0);
return static_cast<U>(-0.0);
}
if (ConvertResult.ec == NAMESPACE_STD::errc::invalid_argument) return NAMESPACE_STD::numeric_limits<U>::quiet_NaN();
return Result;
}
template <CCharType T, CAllocator<T> Allocator>
void TString<T, Allocator>::AppendBool(bool Value)
{
if (Value) Append(LITERAL(ElementType, "True"));
else Append(LITERAL(ElementType, "False"));
}
template <CCharType T, CAllocator<T> Allocator>
template <CIntegral U> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
void TString<T, Allocator>::AppendInt(U Value, unsigned Base)
{
checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base."));
constexpr const ElementType* DigitToChar = LITERAL(ElementType, "0123456789ABCDEF");
using UnsignedU = TMakeUnsigned<U>;
UnsignedU Unsigned = static_cast<UnsignedU>(Value);
bool bNegative = false;
if constexpr (CSigned<U>)
{
if (Value < 0)
{
bNegative = true;
Unsigned = static_cast<UnsignedU>(-Unsigned);
}
}
constexpr size_t BufferSize = sizeof(UnsignedU) * 8 + (CSigned<U> ? 1 : 0);
ElementType Buffer[BufferSize];
ElementType* Iter = Buffer + BufferSize;
switch (Base)
{
case 0x02: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00001)); Unsigned >>= 1; } while (Unsigned != 0); break;
case 0x04: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00011)); Unsigned >>= 2; } while (Unsigned != 0); break;
case 0x08: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00111)); Unsigned >>= 3; } while (Unsigned != 0); break;
case 0x10: do { *--Iter = DigitToChar[Unsigned & 0b01111]; Unsigned >>= 4; } while (Unsigned != 0); break;
case 0X20: do { *--Iter = DigitToChar[Unsigned & 0b11111]; Unsigned >>= 5; } while (Unsigned != 0); break;
case 3:
case 5:
case 6:
case 7:
case 9:
case 10: do { *--Iter = static_cast<ElementType>('0' + Unsigned % Base); Unsigned = static_cast<UnsignedU>(Unsigned / Base); } while (Unsigned != 0); break;
default: do { *--Iter = DigitToChar[Unsigned % Base]; Unsigned = static_cast<UnsignedU>(Unsigned / Base); } while (Unsigned != 0); break;
}
if constexpr (CSigned<U>) if (bNegative) *--Iter = LITERAL(T, '-');
Append(Iter, Buffer + BufferSize);
}
NAMESPACE_PRIVATE_BEGIN
template <CCharType T, size_t Overload>
struct TStringFloatSerializer
{
static FORCEINLINE void Do(auto& Result, auto Value, bool bFixed, bool bScientific, unsigned Precision)
{
NAMESPACE_STD::chars_format Format;
if constexpr (Overload >= 1)
{
if ( bFixed && bScientific) Format = NAMESPACE_STD::chars_format::general;
else if ( bFixed && !bScientific) Format = NAMESPACE_STD::chars_format::fixed;
else if (!bFixed && bScientific) Format = NAMESPACE_STD::chars_format::scientific;
else Format = NAMESPACE_STD::chars_format::hex;
}
constexpr size_t StartingBufferSize = 64;
TArray<char, TInlineAllocator<StartingBufferSize>> Buffer(StartingBufferSize / 2);
NAMESPACE_STD::to_chars_result ConvertResult;
do
{
Buffer.SetNum(Buffer.Num() * 2);
if constexpr (Overload == 0) ConvertResult = NAMESPACE_STD::to_chars(ToAddress(Buffer.Begin()), ToAddress(Buffer.End()), Value);
else if constexpr (Overload == 1) ConvertResult = NAMESPACE_STD::to_chars(ToAddress(Buffer.Begin()), ToAddress(Buffer.End()), Value, Format);
else ConvertResult = NAMESPACE_STD::to_chars(ToAddress(Buffer.Begin()), ToAddress(Buffer.End()), Value, Format, Precision);
}
while (ConvertResult.ec == NAMESPACE_STD::errc::value_too_large);
Buffer.SetNum(ConvertResult.ptr - Buffer.GetData());
const bool bNegative = Buffer[0] == '-';
const char* Iter = Buffer.GetData() + (bNegative ? 1 : 0);
if (*Iter == 'i')
{
if (bNegative) Result.Append(LITERAL(T, "-Infinity"));
else Result.Append(LITERAL(T, "Infinity"));
return;
}
if (*Iter == 'n')
{
if (bNegative) Result.Append(LITERAL(T, "-NaN"));
else Result.Append(LITERAL(T, "NaN"));
return;
}
unsigned Base;
if constexpr (Overload == 0) Base = 10;
else Base = Format == NAMESPACE_STD::chars_format::hex ? 16 : 10;
for (char& Char : Buffer)
{
const auto Digit = FChar::ToDigit(Char, Base);
if (Digit) Char = *FChar::FromDigit(*Digit, Base);
}
Result.Append(Buffer.Begin(), Buffer.End());
}
};
NAMESPACE_PRIVATE_END
template <CCharType T, CAllocator<T> Allocator> template <CFloatingPoint U> requires (!CConst<U> && !CVolatile<U>)
void TString<T, Allocator>::AppendFloat(U Value)
{
NAMESPACE_PRIVATE::TStringFloatSerializer<ElementType, 0>::Do(*this, Value, false, false, 0);
}
template <CCharType T, CAllocator<T> Allocator> template <CFloatingPoint U> requires (!CConst<U> && !CVolatile<U>)
void TString<T, Allocator>::AppendFloat(U Value, bool bFixed, bool bScientific)
{
NAMESPACE_PRIVATE::TStringFloatSerializer<ElementType, 1>::Do(*this, Value, bFixed, bScientific, 0);
}
template <CCharType T, CAllocator<T> Allocator> template <CFloatingPoint U> requires (!CConst<U> && !CVolatile<U>)
void TString<T, Allocator>::AppendFloat(U Value, bool bFixed, bool bScientific, unsigned Precision)
{
NAMESPACE_PRIVATE::TStringFloatSerializer<ElementType, 2>::Do(*this, Value, bFixed, bScientific, Precision);
}
NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft) NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END NAMESPACE_REDCRAFT_END

View File

@ -1022,29 +1022,191 @@ public:
public: public:
/** @return true if the string only contains valid characters, false otherwise. */ /** @return true if the string only contains valid characters, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsValid() const NODISCARD FORCEINLINE bool IsValid() const
{ {
return TStringView<ElementType>(*this).IsValid(); return TStringView<ElementType>(*this).IsValid();
} }
/** @return true if the string only contains ASCII characters, false otherwise. */ /** @return true if the string only contains ASCII characters, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsASCII() const NODISCARD FORCEINLINE bool IsASCII() const
{ {
return TStringView<ElementType>(*this).IsASCII(); return TStringView<ElementType>(*this).IsASCII();
} }
/** @return true if the string only contains numeric characters, false otherwise. */ /** @return true if the string only contains numeric characters, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsNumeric() const NODISCARD FORCEINLINE bool IsNumeric(unsigned Base = 10) const
{
return TStringView<ElementType>(*this).IsNumeric();
}
/** @return true if the string only contains numeric characters, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsNumeric(unsigned Base) const
{ {
return TStringView<ElementType>(*this).IsNumeric(Base); return TStringView<ElementType>(*this).IsNumeric(Base);
} }
public:
/**
* Converts a boolean value into a string.
*
* - true becomes "True".
* - false becomes "False".
*
* @return The string containing the boolean value.
*/
NODISCARD static FORCEINLINE TString FromBool(bool Value)
{
TString Result;
Result.AppendBool(Value);
return Result;
}
/**
* Converts an integer value into a string.
*
* @param Base - The base of the number, between [2, 36].
*
* @return The string containing the integer value.
*/
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
NODISCARD static FORCEINLINE TString FromInt(U Value, unsigned Base = 10)
{
checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base."));
TString Result;
Result.AppendInt(Value, Base);
return Result;
}
/**
* Converts a floating-point value into a string.
* The string is formatted using the shortest representation in fixed-point or scientific notation.
*
* @return The string containing the floating-point value.
*/
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD static FORCEINLINE TString FromFloat(U Value)
{
TString Result;
Result.AppendFloat(Value);
return Result;
}
/**
* Converts a floating-point value into a string.
* The string is formatted using the shortest representation in fixed-point or scientific notation.
* The string is formatted using the hex representation if bFixed and bScientific are false.
*
* @param bFixed - The fixed-point format.
* @param bScientific - The scientific notation.
*
* @return The string containing the floating-point value.
*/
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD static FORCEINLINE TString FromFloat(U Value, bool bFixed, bool bScientific)
{
TString Result;
Result.AppendFloat(Value, bFixed, bScientific);
return Result;
}
/**
* Converts a floating-point value into a string.
* The string is formatted using the shortest representation in fixed-point or scientific notation.
* The string is formatted using the hex representation if bFixed and bScientific are false.
*
* @param bFixed - The fixed-point format.
* @param bScientific - The scientific notation.
* @param Precision - The number of digits after the decimal point.
* @return
*/
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
NODISCARD static FORCEINLINE TString FromFloat(U Value, bool bFixed, bool bScientific, unsigned Precision)
{
TString Result;
Result.AppendFloat(Value, bFixed, bScientific, Precision);
return Result;
}
/** Converts a boolean value into a string and appends it to the string. */
void AppendBool(bool Value);
/** Converts an integer value into a string and appends it to the string. */
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
void AppendInt(U Value, unsigned Base = 10);
/** Converts a floating-point value into a string and appends it to the string. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
void AppendFloat(U Value);
/** Converts a floating-point value into a string and appends it to the string. */
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
void AppendFloat(U Value, bool bFixed, bool bScientific);
/** Converts a floating-point value into a string and appends it to the string. */
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.
*
* - 1, "true", "True", "TRUE" and non-zero integers become true.
* - 0, "false", "False", "FALSE" and unparsable values become false.
*
* @return The boolean value.
*/
NODISCARD FORCEINLINE bool ToBool() const
{
return TStringView<ElementType>(*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<ElementType>(*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<ElementType>(*this).template ToFloat<U>(bFixed, bScientific);
}
public: public:
/** /**
@ -1067,7 +1229,7 @@ public:
* @return The number of objects successfully parsed. * @return The number of objects successfully parsed.
*/ */
template <typename... Ts> template <typename... Ts>
size_t Parse(TStringView<ElementType> Fmt, Ts&... Args) const FORCEINLINE size_t Parse(TStringView<ElementType> Fmt, Ts&... Args) const
{ {
return TStringView(*this).Parse(Fmt, Args...); return TStringView(*this).Parse(Fmt, Args...);
} }

View File

@ -402,18 +402,7 @@ public:
} }
/** @return true if the string only contains numeric characters, false otherwise. */ /** @return true if the string only contains numeric characters, false otherwise. */
NODISCARD constexpr bool IsNumeric() const NODISCARD constexpr bool IsNumeric(unsigned Base = 10) const
{
for (ElementType Char : *this)
{
if (!TChar<ElementType>::IsDigit(Char)) return false;
}
return true;
}
/** @return true if the string only contains numeric characters, false otherwise. */
NODISCARD constexpr bool IsNumeric(unsigned Base) const
{ {
for (ElementType Char : *this) for (ElementType Char : *this)
{ {
@ -423,6 +412,52 @@ public:
return true; return true;
} }
public:
/**
* Converts a string into a boolean value.
*
* - 1, "true", "True", "TRUE" and non-zero integers become true.
* - 0, "false", "False", "FALSE" and unparsable values become false.
*
* @return The boolean value.
*/
NODISCARD constexpr bool ToBool() const;
/**
* 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 constexpr U ToInt(unsigned Base = 10) const;
/**
* 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 constexpr U ToFloat(bool bFixed = true, bool bScientific = true) const;
public: public:
/** /**