refactor(strings): refactor and simplify string parsing functions
This commit is contained in:
parent
262ce1bb67
commit
a3cebf03ec
@ -2,9 +2,10 @@
|
||||
|
||||
#include "Strings/Char.h"
|
||||
#include "Memory/Memory.h"
|
||||
#include "Strings/String.h"
|
||||
#include "Numerics/Numerics.h"
|
||||
#include "Strings/String.h"
|
||||
#include "Strings/StringView.h"
|
||||
#include "Strings/Convert.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
@ -196,10 +197,11 @@ void TestStringView()
|
||||
{
|
||||
always_check( LITERAL_VIEW(T, "012345678900").IsASCII());
|
||||
always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsASCII());
|
||||
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));
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
@ -211,7 +213,7 @@ void TestStringView()
|
||||
Test(InPlaceType<unicodechar>);
|
||||
}
|
||||
|
||||
void TestTemplateString()
|
||||
void TestString()
|
||||
{
|
||||
auto Test = []<typename T>(TInPlaceType<T>)
|
||||
{
|
||||
@ -448,38 +450,17 @@ void TestTemplateString()
|
||||
Test(InPlaceType<unicodechar>);
|
||||
}
|
||||
|
||||
void TestStringConversion()
|
||||
void TestConvert()
|
||||
{
|
||||
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(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 (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 (CFloatingPoint<U>)
|
||||
{
|
||||
@ -554,6 +535,60 @@ void TestStringConversion()
|
||||
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"));
|
||||
@ -579,37 +614,6 @@ 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>);
|
||||
@ -626,7 +630,8 @@ void TestString()
|
||||
{
|
||||
NAMESPACE_PRIVATE::TestChar();
|
||||
NAMESPACE_PRIVATE::TestStringView();
|
||||
NAMESPACE_PRIVATE::TestTemplateString();
|
||||
NAMESPACE_PRIVATE::TestString();
|
||||
NAMESPACE_PRIVATE::TestConvert();
|
||||
NAMESPACE_PRIVATE::TestStringConversion();
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
487
Redcraft.Utility/Source/Public/Strings/Convert.h
Normal file
487
Redcraft.Utility/Source/Public/Strings/Convert.h
Normal file
@ -0,0 +1,487 @@
|
||||
#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)
|
@ -1144,22 +1144,64 @@ public:
|
||||
return TStringView<FElementType>(*this).IsASCII();
|
||||
}
|
||||
|
||||
/** @return true if the string can be fully represented as a boolean value, false otherwise. */
|
||||
/** @return true if the string can be converted to a boolean value, false otherwise. */
|
||||
NODISCARD FORCEINLINE bool IsBoolean() const
|
||||
{
|
||||
return TStringView<FElementType>(*this).IsBoolean();
|
||||
}
|
||||
|
||||
/** @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 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 TStringView<FElementType>(*this).IsInteger(Base, bSigned);
|
||||
return TStringView<FElementType>(*this).template IsInteger<U>(Base);
|
||||
}
|
||||
|
||||
/** @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 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 TStringView<FElementType>(*this).IsFloatingPoint(bFixed, bScientific, bSigned);
|
||||
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);
|
||||
}
|
||||
|
||||
public:
|
||||
@ -1275,105 +1317,6 @@ 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:
|
||||
|
||||
/**
|
||||
@ -1398,35 +1341,6 @@ 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,6 +12,7 @@
|
||||
#include "Iterators/BasicIterator.h"
|
||||
#include "Iterators/Sentinel.h"
|
||||
#include "Strings/Char.h"
|
||||
#include "Strings/Convert.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
#include <cstring>
|
||||
@ -529,133 +530,83 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return true if the string can be fully represented as a boolean value, false otherwise. */
|
||||
/** @return true if the string can be converted to a boolean value, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsBoolean() const
|
||||
{
|
||||
TStringView View = *this;
|
||||
bool Temp;
|
||||
|
||||
Ignore = View.ToBoolAndTrim();
|
||||
|
||||
return View.IsEmpty();
|
||||
return Algorithms::Parse(*this, Temp);
|
||||
}
|
||||
|
||||
/** @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
|
||||
/** @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
|
||||
{
|
||||
TStringView View = *this;
|
||||
U Temp;
|
||||
|
||||
if (View.StartsWith(LITERAL(FElementType, '-')))
|
||||
{
|
||||
if (bSigned) View.RemovePrefix(1);
|
||||
else return false;
|
||||
}
|
||||
|
||||
Ignore = View.ToIntAndTrim(Base);
|
||||
|
||||
return View.IsEmpty();
|
||||
return Algorithms::Parse(*this, Temp, Base);
|
||||
}
|
||||
|
||||
/** @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
|
||||
/** @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
|
||||
{
|
||||
TStringView View = *this;
|
||||
U Temp;
|
||||
|
||||
if (View.StartsWith(LITERAL(FElementType, '-')))
|
||||
{
|
||||
if (bSigned) View.RemovePrefix(1);
|
||||
else return false;
|
||||
}
|
||||
|
||||
Ignore = View.ToFloatAndTrim(bFixed, bScientific);
|
||||
|
||||
return View.IsEmpty();
|
||||
return Algorithms::Parse(*this, Temp, bFixed, bScientific, bHex);
|
||||
}
|
||||
|
||||
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
|
||||
/** Converts the string into a boolean value. */
|
||||
NODISCARD FORCEINLINE constexpr bool ToBool() const
|
||||
{
|
||||
return TStringView(*this).ToBoolAndTrim();
|
||||
bool Result;
|
||||
|
||||
verifyf(Algorithms::Parse(*this, Result), TEXT("Illegal conversion. Please check the IsBoolean()."));
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** Converts the string into an integer value. */
|
||||
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
|
||||
NODISCARD constexpr U ToInt(unsigned Base = 10) const
|
||||
NODISCARD FORCEINLINE constexpr U ToInt(uint Base = 0) const
|
||||
{
|
||||
return TStringView(*this).ToIntAndTrim<U>(Base);
|
||||
U Result;
|
||||
|
||||
verifyf(Algorithms::Parse(*this, Result, Base), TEXT("Illegal conversion. Please check the IsInteger()."));
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** Converts the string into a floating-point value. */
|
||||
template <CFloatingPoint U = float> requires (!CConst<U> && !CVolatile<U>)
|
||||
NODISCARD constexpr U ToFloat(bool bFixed = true, bool bScientific = true) const
|
||||
NODISCARD FORCEINLINE constexpr U ToFloat(bool bFixed = true, bool bScientific = true, bool bHex = true) const
|
||||
{
|
||||
return TStringView(*this).ToFloatAndTrim<U>(bFixed, bScientific);
|
||||
U Result;
|
||||
|
||||
verifyf(Algorithms::Parse(*this, Result, bFixed, bScientific, bHex), TEXT("Illegal conversion. Please check the IsFloatingPoint()."));
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/** Converts a string into a boolean value and remove the parsed substring. */
|
||||
NODISCARD constexpr bool ToBoolAndTrim();
|
||||
/** Parse the string into a boolean value. */
|
||||
NODISCARD FORCEINLINE constexpr bool Parse(bool& Value)
|
||||
{
|
||||
return Algorithms::Parse(*this, Value);
|
||||
}
|
||||
|
||||
/** Converts a string into an integer value and remove the parsed substring. */
|
||||
/** Parse the string into an integer value. */
|
||||
template <CIntegral U = int> requires (!CSameAs<U, bool> && !CConst<U> && !CVolatile<U>)
|
||||
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
|
||||
NODISCARD FORCEINLINE constexpr bool Parse(U& Value, uint Base = 0)
|
||||
{
|
||||
return TStringView(*this).ParseAndTrim(Fmt, Args...);
|
||||
return Algorithms::Parse(*this, Value, Base);
|
||||
}
|
||||
|
||||
/** Parse a string using a format string to objects and remove the parsed substring. */
|
||||
template <typename... Ts>
|
||||
size_t ParseAndTrim(TStringView Fmt, Ts&... 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);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user