From 0e8d4cb6ac679fd52a320aae9d1a478e47728116 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Mon, 30 Dec 2019 17:28:35 -0600 Subject: [PATCH] StringUtil: Make TryParse of floats handle comma and dot decimal separators. --- Source/Core/Common/StringUtil.cpp | 45 ---------------------- Source/Core/Common/StringUtil.h | 63 +++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 56 deletions(-) diff --git a/Source/Core/Common/StringUtil.cpp b/Source/Core/Common/StringUtil.cpp index 3eeb6d04fe..2369208f45 100644 --- a/Source/Core/Common/StringUtil.cpp +++ b/Source/Core/Common/StringUtil.cpp @@ -235,51 +235,6 @@ std::string_view StripQuotes(std::string_view s) return s; } -bool TryParse(const std::string& str, u16* const output) -{ - u64 value; - if (!TryParse(str, &value)) - return false; - - if (value >= 0x10000ull && value <= 0xFFFFFFFFFFFF0000ull) - return false; - - *output = static_cast(value); - return true; -} - -bool TryParse(const std::string& str, u32* const output) -{ - u64 value; - if (!TryParse(str, &value)) - return false; - - if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull) - return false; - - *output = static_cast(value); - return true; -} - -bool TryParse(const std::string& str, u64* const output) -{ - char* end_ptr = nullptr; - - // Set errno to a clean slate - errno = 0; - - u64 value = strtoull(str.c_str(), &end_ptr, 0); - - if (end_ptr == nullptr || *end_ptr != '\0') - return false; - - if (errno == ERANGE) - return false; - - *output = value; - return true; -} - bool TryParse(const std::string& str, bool* const output) { float value; diff --git a/Source/Core/Common/StringUtil.h b/Source/Core/Common/StringUtil.h index b651910767..c47fd1f620 100644 --- a/Source/Core/Common/StringUtil.h +++ b/Source/Core/Common/StringUtil.h @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include #include #include @@ -47,20 +49,60 @@ std::string ArrayToString(const u8* data, u32 size, int line_len = 20, bool spac std::string_view StripSpaces(std::string_view s); std::string_view StripQuotes(std::string_view s); +std::string ReplaceAll(std::string result, std::string_view src, std::string_view dest); + bool TryParse(const std::string& str, bool* output); -bool TryParse(const std::string& str, u16* output); -bool TryParse(const std::string& str, u32* output); -bool TryParse(const std::string& str, u64* output); -template -static bool TryParse(const std::string& str, N* const output) +template || std::is_enum_v>* = nullptr> +bool TryParse(const std::string& str, T* output) { - std::istringstream iss(str); - // is this right? not doing this breaks reading floats on locales that use different decimal - // separators - iss.imbue(std::locale("C")); + char* end_ptr = nullptr; - N tmp; + // Set errno to a clean slate. + errno = 0; + + // Read a u64 for unsigned types and s64 otherwise. + using ReadType = std::conditional_t, u64, s64>; + ReadType value; + + if constexpr (std::is_unsigned_v) + value = std::strtoull(str.c_str(), &end_ptr, 0); + else + value = std::strtoll(str.c_str(), &end_ptr, 0); + + // Fail if the end of the string wasn't reached. + if (end_ptr == nullptr || *end_ptr != '\0') + return false; + + // Fail if the value was out of 64-bit range. + if (errno == ERANGE) + return false; + + using LimitsType = typename std::conditional_t, std::underlying_type, + std::common_type>::type; + // Fail if outside numeric limits. + if (value < std::numeric_limits::min() || + value > std::numeric_limits::max()) + { + return false; + } + + *output = static_cast(value); + return true; +} + +template >* = nullptr> +bool TryParse(std::string str, T* const output) +{ + // Replace commas with dots. + std::istringstream iss(ReplaceAll(std::move(str), ",", ".")); + + // Use "classic" locale to force a "dot" decimal separator. + iss.imbue(std::locale::classic()); + + T tmp; + + // Succeed if a value was read and the entire string was used. if (iss >> tmp && iss.eof()) { *output = tmp; @@ -118,7 +160,6 @@ bool SplitPath(std::string_view full_path, std::string* path, std::string* filen void BuildCompleteFilename(std::string& complete_filename, std::string_view path, std::string_view filename); -std::string ReplaceAll(std::string result, std::string_view src, std::string_view dest); bool StringBeginsWith(std::string_view str, std::string_view begin); bool StringEndsWith(std::string_view str, std::string_view end);