feat(string): add string and arithmetic conversion functions

This commit is contained in:
Redstone1024 2024-11-11 22:30:15 +08:00
parent 49feb0b12b
commit fe60fc33e0
4 changed files with 637 additions and 26 deletions

View File

@ -536,6 +536,70 @@ void TestStringConversion()
CheckParseFloat(InPlaceType<float>);
CheckParseFloat(InPlaceType<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>);

View File

@ -8,9 +8,10 @@
#include <cmath>
#include <limits>
#include <charconv>
#pragma warning(push)
#pragma warning(disable : 4146)
#pragma warning(disable : 4146 4244)
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
@ -33,7 +34,7 @@ NAMESPACE_PRIVATE_BEGIN
template <CCharType T, bool bIsFormat>
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)>;
@ -119,7 +120,7 @@ struct TStringHelper
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)>;
@ -328,7 +329,7 @@ struct TStringHelper
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;
@ -578,6 +579,355 @@ size_t TStringView<T>::Parse(TStringView Fmt, Ts&... Args) const
#undef ESCAPE_LEFT_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(Redcraft)
NAMESPACE_REDCRAFT_END

View File

@ -1022,29 +1022,191 @@ public:
public:
/** @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 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 true if the string only contains numeric characters, false otherwise. */
NODISCARD FORCEINLINE constexpr bool IsNumeric() 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
NODISCARD FORCEINLINE bool IsNumeric(unsigned Base = 10) const
{
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:
/**
@ -1067,7 +1229,7 @@ public:
* @return The number of objects successfully parsed.
*/
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...);
}

View File

@ -402,18 +402,7 @@ public:
}
/** @return true if the string only contains numeric characters, false otherwise. */
NODISCARD constexpr bool IsNumeric() 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
NODISCARD constexpr bool IsNumeric(unsigned Base = 10) const
{
for (ElementType Char : *this)
{
@ -423,6 +412,52 @@ public:
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:
/**