From ae2b05be5c594ddcdf607b06d483de7aa7449a45 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 31 Oct 2023 00:04:27 +0100 Subject: [PATCH] Fix more compatibility problems in C++17 wxString::ToLong() Unlike the traditional C functions, std::from_chars() doesn't skip leading whitespace and doesn't accept the leading "+" sign, so we need to skip them explicitly to preserve the behaviour of ToLong() in the previous wxWidgets versions. --- src/common/string.cpp | 24 +++++++++++++++++------- tests/strings/strings.cpp | 3 +++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/common/string.cpp b/src/common/string.cpp index bcd78b67bd..f2949268e3 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -1580,15 +1580,25 @@ bool wxString::ToDouble(double *pVal) const namespace { -// Helper of ToCLong() and ToCULong() taking care of base-related stuff: -// because from_chars() doesn't recognize base==0 and doesn't recognize "0x" -// prefix even if base 16 is explicitly specified, we need to skip the prefix -// indicating the base to use if it's present and adjust "base" itself instead. +// Helper of ToCLong() and ToCULong() taking care of prefix and base-related +// stuff: because from_chars() doesn't skip leading whitespace and doesn't +// recognize base==0 nor "0x" prefix even if base 16 is explicitly specified, +// we need to skip the leading space and prefix indicating the base to use if +// it's present and adjust "base" itself instead. // // Return false if base is already specified but is incompatible with the // prefix used. -bool SetBaseAndSkipPrefix(int& base, const char*& start, const char* end) +bool SkipOptPrefixAndSetBase(int& base, const char*& start, const char* end) { + // Start by skipping whitespace. + while ( wxSafeIsspace(*start) ) + ++start; + + // Also skip optional "+" which std::from_chars() doesn't accept neither. + if ( *start == '+' ) + ++start; + + // Then check for the base prefix. if ( end - start > 1 && *start == '0' ) { ++start; @@ -1623,7 +1633,7 @@ bool wxString::ToCLong(long *pVal, int base) const auto start = buf.data(); const auto end = start + buf.length(); - if ( !SetBaseAndSkipPrefix(base, start, end) ) + if ( !SkipOptPrefixAndSetBase(base, start, end) ) return false; const auto res = std::from_chars(start, end, *pVal, base); @@ -1639,7 +1649,7 @@ bool wxString::ToCULong(unsigned long *pVal, int base) const auto start = buf.data(); const auto end = start + buf.length(); - if ( !SetBaseAndSkipPrefix(base, start, end) ) + if ( !SkipOptPrefixAndSetBase(base, start, end) ) return false; // Extra complication: for compatibility reasons, this function does accept diff --git a/tests/strings/strings.cpp b/tests/strings/strings.cpp index 43afe86eba..e8f0550a3f 100644 --- a/tests/strings/strings.cpp +++ b/tests/strings/strings.cpp @@ -580,6 +580,9 @@ static const struct ToLongData { wxT("-1"), -1, Number_Signed | Number_Long }, // this is surprising but consistent with strtoul() behaviour { wxT("-1"), (TestValue_t)ULONG_MAX, Number_Unsigned | Number_Long }, + // a couple of edge cases + { wxT(" +1"), 1, Number_Ok }, + { wxT(" -1"), (TestValue_t)ULONG_MAX, Number_Unsigned | Number_Long }, // this must overflow, even with 64 bit long { wxT("922337203685477580711"), 0, Number_Invalid },