diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-10-17 14:06:48 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-10-17 14:06:48 +0800 |
commit | 5c5c496b605886b286d1b99e0f9e28ec02117ad5 (patch) | |
tree | c22b07e81ba179d7cc8790656abddbcc56b5d704 /include | |
parent | 32aa6f116acc6e3e20a1ec76cef45b29f7005ad7 (diff) | |
download | cru-5c5c496b605886b286d1b99e0f9e28ec02117ad5.tar.gz cru-5c5c496b605886b286d1b99e0f9e28ec02117ad5.tar.bz2 cru-5c5c496b605886b286d1b99e0f9e28ec02117ad5.zip |
Use std::from_chars.
Diffstat (limited to 'include')
-rw-r--r-- | include/cru/base/String.h | 47 | ||||
-rw-r--r-- | include/cru/base/StringToNumberConverter.h | 106 | ||||
-rw-r--r-- | include/cru/base/StringUtil.h | 88 |
3 files changed, 88 insertions, 153 deletions
diff --git a/include/cru/base/String.h b/include/cru/base/String.h index 7d462a6e..313a3ce2 100644 --- a/include/cru/base/String.h +++ b/include/cru/base/String.h @@ -3,7 +3,6 @@ #include "Buffer.h" #include "Range.h" -#include "StringToNumberConverter.h" #include "StringUtil.h" #include <filesystem> @@ -11,7 +10,6 @@ #include <iterator> #include <string> #include <string_view> -#include <type_traits> #include <vector> namespace cru { @@ -215,22 +213,6 @@ class CRU_BASE_API String { Range RangeFromCodeUnitToCodePoint(Range code_unit_range) const; Range RangeFromCodePointToCodeUnit(Range code_point_range) const; - template <typename TInteger> - std::enable_if_t<std::is_signed_v<TInteger>, TInteger> ParseToInteger( - Index* processed_characters_count, unsigned flags, int base) const; - - int ParseToInt(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}, int base = 0) const; - long long ParseToLongLong(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}, int base = 0) const; - - float ParseToFloat(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}) const; - double ParseToDouble(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}) const; - std::vector<float> ParseToFloatList(value_type separator = u' ') const; - std::vector<double> ParseToDoubleList(value_type separator = u' ') const; - #ifdef CRU_PLATFORM_WINDOWS const wchar_t* WinCStr() const { return reinterpret_cast<const wchar_t*>(c_str()); @@ -351,29 +333,6 @@ class CRU_BASE_API StringView { Range RangeFromCodeUnitToCodePoint(Range code_unit_range) const; Range RangeFromCodePointToCodeUnit(Range code_point_range) const; - template <typename TInteger> - std::enable_if_t<std::is_signed_v<TInteger>, TInteger> ParseToInteger( - Index* processed_characters_count, StringToNumberFlag flags, - int base) const { - auto utf8_string = ToUtf8(); - auto result = StringToIntegerConverter(flags, base) - .Parse(utf8_string.data(), utf8_string.size(), - processed_characters_count); - return result.negate ? -result.value : result.value; - } - - int ParseToInt(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}, int base = 0) const; - long long ParseToLongLong(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}, int base = 0) const; - - float ParseToFloat(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}) const; - double ParseToDouble(Index* processed_characters_count = nullptr, - StringToNumberFlag flags = {}) const; - std::vector<float> ParseToFloatList(value_type separator = u' ') const; - std::vector<double> ParseToDoubleList(value_type separator = u' ') const; - std::string ToUtf8() const; Buffer ToUtf8Buffer(bool end_zero = true) const; @@ -464,12 +423,6 @@ inline Index Utf16NextWord(StringView str, Index position, String CRU_BASE_API ToLower(StringView s); String CRU_BASE_API ToUpper(StringView s); -template <typename TInteger> -std::enable_if_t<std::is_signed_v<TInteger>, TInteger> String::ParseToInteger( - Index* processed_characters_count, unsigned flags, int base) const { - View().ParseToInteger<TInteger>(processed_characters_count, flags, base); -} - } // namespace cru template <> diff --git a/include/cru/base/StringToNumberConverter.h b/include/cru/base/StringToNumberConverter.h deleted file mode 100644 index 64c29971..00000000 --- a/include/cru/base/StringToNumberConverter.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once -#include "Base.h" -#include "Bitmask.h" - -#include <cstddef> -#include <ostream> - -namespace cru { -namespace details { -struct StringToNumberFlagTag {}; -} // namespace details - -using StringToNumberFlag = Bitmask<details::StringToNumberFlagTag>; - -struct StringToNumberFlags { - constexpr static StringToNumberFlag kAllowLeadingSpaces = - StringToNumberFlag::FromOffset(1); - constexpr static StringToNumberFlag kAllowTrailingSpaces = - StringToNumberFlag::FromOffset(2); - constexpr static StringToNumberFlag kAllowTrailingJunk = - StringToNumberFlag::FromOffset(3); - constexpr static StringToNumberFlag kAllowLeadingZeroForInteger = - StringToNumberFlag::FromOffset(4); - constexpr static StringToNumberFlag kThrowOnError = - StringToNumberFlag::FromOffset(5); -}; - -template <typename TResult> -struct IStringToNumberConverter : virtual Interface { - virtual TResult Parse(const char* str, Index size, - Index* processed_characters_count) const = 0; - - template <std::size_t Size> - TResult Parse(const char (&str)[Size], - Index* processed_characters_count) const { - return Parse(str, Size - 1, processed_characters_count); - } -}; - -struct CRU_BASE_API StringToIntegerResult { - StringToIntegerResult() = default; - StringToIntegerResult(bool negate, unsigned long long value) - : negate(negate), value(value) {} - - bool negate; - unsigned long long value; -}; - -inline bool CRU_BASE_API operator==(const StringToIntegerResult& left, - const StringToIntegerResult& right) { - return left.negate == right.negate && left.value == right.value; -} - -inline bool CRU_BASE_API operator!=(const StringToIntegerResult& left, - const StringToIntegerResult& right) { - return !(left == right); -} - -CRU_BASE_API inline std::ostream& operator<<(std::ostream& stream, const StringToIntegerResult& result) { - return stream << "StringToIntegerConverterImplResult(" - << (result.negate ? "-" : "") << result.value << ")"; -} - -/** - * \brief A converter that convert number into long long. - */ -struct CRU_BASE_API StringToIntegerConverter - : IStringToNumberConverter<StringToIntegerResult> { - public: - explicit StringToIntegerConverter(StringToNumberFlag flags, int base = 0) - : flags(flags), base(base) {} - - bool CheckParams() const; - - /** - * \brief Convert string to long long. - * \param str The string to convert. - * \param size The size of the string. - * \param processed_characters_count The number of characters that were - * processed. Or nullptr to not retrieve. - */ - StringToIntegerResult Parse(const char* str, Index size, - Index* processed_characters_count) const override; - using IStringToNumberConverter<StringToIntegerResult>::Parse; - - StringToNumberFlag flags; - /** - * \brief The base of the number used for parse or 0 for auto detect. - * \remarks Base can only be of range [2, 36] or 0. If base is 0, decimal is - * assumed by default ,or if str is started with "0x" or "0X" hexadecimal is - * assumed, or if str is started with a single "0" octoral is assumed, or if - * str is started with "0b" or "0B" binary is assumed. Otherwise it is an - * error. - */ - int base; -}; - -struct CRU_BASE_API StringToFloatConverter { - StringToFloatConverter(StringToNumberFlag flags) : flags(flags) {} - - double Parse(const char* str, Index size, - Index* processed_characters_count) const; - - StringToNumberFlag flags; -}; -} // namespace cru diff --git a/include/cru/base/StringUtil.h b/include/cru/base/StringUtil.h index 2b1b5914..258d884e 100644 --- a/include/cru/base/StringUtil.h +++ b/include/cru/base/StringUtil.h @@ -2,11 +2,15 @@ #include "Base.h" #include "Bitmask.h" +#include <algorithm> +#include <cctype> +#include <charconv> #include <compare> #include <format> #include <functional> #include <string> #include <string_view> +#include <system_error> #include <type_traits> #include <vector> @@ -31,6 +35,90 @@ struct SplitOptions { std::vector<std::string> Split(std::string_view str, std::string_view sep, SplitOption options = {}); +namespace details { +struct ParseToNumberFlagTag {}; +} // namespace details + +using ParseToNumberFlag = Bitmask<details::ParseToNumberFlagTag>; + +struct ParseToNumberFlags { + constexpr static ParseToNumberFlag AllowLeadingSpaces = + ParseToNumberFlag::FromOffset(1); + constexpr static ParseToNumberFlag AllowTrailingSpaces = + ParseToNumberFlag::FromOffset(2); + constexpr static ParseToNumberFlag AllowTrailingJunk = + ParseToNumberFlag::FromOffset(3); +}; + +template <typename T> +struct ParseToNumberResult { + bool valid; + T value; + Index processed_char_count; + std::string message; +}; + +template <typename T> +ParseToNumberResult<T> ParseToNumber(std::string_view str, + ParseToNumberFlag flags = {}) { + ParseToNumberResult<T> result{}; + + const char* ptr = str.data(); + const char* const begin = str.data(); + const char* const end = str.data() + str.size(); + if (flags.Has(ParseToNumberFlags::AllowLeadingSpaces)) { + while (ptr != str.data() + str.size() && isspace(*ptr)) { + ptr++; + } + } + + if (ptr == end) { + result.valid = false; + result.message = + "Parsing reached the end (after reading all leading spaces)."; + return result; + } + + auto parse_result = + std::from_chars(ptr, str.data() + str.size(), result.value); + if (parse_result.ec == std::errc::invalid_argument) { + result.valid = false; + result.message = "Not a valid number."; + return result; + } else if (parse_result.ec == std::errc::result_out_of_range) { + result.valid = false; + result.message = "Value out of range."; + return result; + } else { + if (parse_result.ptr == end || + flags.Has(ParseToNumberFlags::AllowTrailingJunk) || + (flags.Has(ParseToNumberFlags::AllowTrailingSpaces) && + IsSpace(std::string_view(parse_result.ptr, end)))) { + result.valid = true; + result.processed_char_count = parse_result.ptr - str.data(); + return result; + } else { + result.valid = false; + result.message = "There are junk trailing characters."; + return result; + } + } +} + +template <typename T> +std::vector<T> ParseToNumberList(std::string_view str, + std::string_view separator = " ") { + auto segs = Split(str, separator, SplitOptions::RemoveSpace); + std::vector<T> result; + for (const auto& seg : segs) { + result.push_back( + ParseToNumber<T>(Trim(seg), ParseToNumberFlags::AllowLeadingSpaces | + ParseToNumberFlags::AllowTrailingSpaces) + .value); + } + return result; +} + template <typename T> struct ImplementFormatterByToString { template <class ParseContext> |