From eb64202ad420deca7eb91d9d2d83a7a46583da52 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 21 Feb 2021 17:28:02 +0100 Subject: [PATCH] Add support for unsigned long long to wxNumberFormatter This is necessary in order to deal with the numbers greater than wxINT64_MAX that can't be represented in just long long. It also allows to implement the intuitive handling of minus sign for the unsigned numbers, i.e. not to accept it in FromString(), unlike the standard functions which do (and parse -1 as 0xffff...fff). Also extend the tests to check for more boundary cases. --- include/wx/numformatter.h | 3 ++ interface/wx/numformatter.h | 8 +++++ src/common/numformatter.cpp | 27 ++++++++++++++++ tests/strings/numformatter.cpp | 57 ++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/include/wx/numformatter.h b/include/wx/numformatter.h index 13b47b210b..24ab702ab7 100644 --- a/include/wx/numformatter.h +++ b/include/wx/numformatter.h @@ -34,6 +34,8 @@ public: static wxString ToString(wxLongLong_t val, int style = Style_WithThousandsSep); #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG + static wxString ToString(wxULongLong_t val, + int style = Style_WithThousandsSep); static wxString ToString(double val, int precision, int style = Style_WithThousandsSep); @@ -46,6 +48,7 @@ public: #ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG static bool FromString(wxString s, wxLongLong_t *val); #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG + static bool FromString(wxString s, wxULongLong_t *val); static bool FromString(wxString s, double *val); diff --git a/interface/wx/numformatter.h b/interface/wx/numformatter.h index c6e0eaffe2..1479bae1c4 100644 --- a/interface/wx/numformatter.h +++ b/interface/wx/numformatter.h @@ -73,6 +73,7 @@ public: //@{ static wxString ToString(long val, int flags = Style_WithThousandsSep); static wxString ToString(long long val, int flags = Style_WithThousandsSep); + static wxString ToString(unsigned long long val, int flags = Style_WithThousandsSep); //@} /** @@ -97,11 +98,18 @@ public: success they return @true and store the result at the location pointed to by @a val (which can't be @NULL), otherwise @false is returned. + Note that the overload taking unsigned long long value is only + available since wxWidgets 3.1.5. Also, unlike wxString::ToULongLong() + and the standard functions such as @c strtoul(), this overload does + @em not accept, i.e. returns @false, for the strings starting with the + minus sign. + @see wxString::ToLong(), wxString::ToDouble() */ //@{ static bool FromString(wxString s, long *val); static bool FromString(wxString s, long long *val); + static bool FromString(wxString s, unsigned long long *val); static bool FromString(wxString s, double *val); //@} diff --git a/src/common/numformatter.cpp b/src/common/numformatter.cpp index 1bf8497712..e57ab02335 100644 --- a/src/common/numformatter.cpp +++ b/src/common/numformatter.cpp @@ -198,6 +198,12 @@ wxString wxNumberFormatter::ToString(wxLongLong_t val, int style) #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG +wxString wxNumberFormatter::ToString(wxULongLong_t val, int style) +{ + return PostProcessIntString(wxString::Format("%" wxLongLongFmtSpec "u", val), + style); +} + wxString wxNumberFormatter::ToString(double val, int precision, int style) { wxString s = wxString::FromDouble(val,precision); @@ -300,6 +306,27 @@ bool wxNumberFormatter::FromString(wxString s, wxLongLong_t *val) #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG +bool wxNumberFormatter::FromString(wxString s, wxULongLong_t *val) +{ + RemoveThousandsSeparators(s); + + // wxString::ToULongLong() does accept minus sign for unsigned integers, + // consistently with the standard functions behaviour, e.g. strtoul() does + // the same thing, but here we really want to accept the "true" unsigned + // numbers only, so check for leading minus, possibly preceded by some + // whitespace. + for ( wxString::const_iterator it = s.begin(); it != s.end(); ++it ) + { + if ( *it == '-' ) + return false; + + if ( *it != ' ' && *it != '\t' ) + break; + } + + return s.ToULongLong(val); +} + bool wxNumberFormatter::FromString(wxString s, double *val) { RemoveThousandsSeparators(s); diff --git a/tests/strings/numformatter.cpp b/tests/strings/numformatter.cpp index d93f6c3244..b9445190dc 100644 --- a/tests/strings/numformatter.cpp +++ b/tests/strings/numformatter.cpp @@ -206,6 +206,10 @@ TEST_CASE_METHOD(NumFormatterTestCase, "NumFormatter::LongLongFromString", "[num CHECK( !wxNumberFormatter::FromString("", &l) ); CHECK( !wxNumberFormatter::FromString("foo", &l) ); CHECK( !wxNumberFormatter::FromString("1.234", &l) ); + CHECK( !wxNumberFormatter::FromString("-", &l) ); + + CHECK( wxNumberFormatter::FromString("0", &l) ); + CHECK( l == 0 ); CHECK( wxNumberFormatter::FromString("123", &l) ); CHECK( l == 123 ); @@ -224,10 +228,63 @@ TEST_CASE_METHOD(NumFormatterTestCase, "NumFormatter::LongLongFromString", "[num CHECK( wxNumberFormatter::FromString("1,234,567", &l) ); CHECK( l == 1234567 ); + + CHECK( wxNumberFormatter::FromString("-123", &l) ); + CHECK( l == -123 ); + + CHECK( wxNumberFormatter::FromString("9223372036854775807", &l) ); + CHECK( l == wxINT64_MAX ); + + CHECK( !wxNumberFormatter::FromString("9223372036854775808", &l) ); } #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG +TEST_CASE_METHOD(NumFormatterTestCase, "NumFormatter::ULongLongFromString", "[numformatter]") +{ + if ( !CanRunTest() ) + return; + + wxULongLong_t u; + CHECK( !wxNumberFormatter::FromString("", &u) ); + CHECK( !wxNumberFormatter::FromString("bar", &u) ); + CHECK( !wxNumberFormatter::FromString("1.234", &u) ); + CHECK( !wxNumberFormatter::FromString("-2", &u) ); + CHECK( !wxNumberFormatter::FromString("-", &u) ); + + CHECK( wxNumberFormatter::FromString("0", &u) ); + CHECK( u == 0 ); + + CHECK( wxNumberFormatter::FromString("123", &u) ); + CHECK( u == 123 ); + + CHECK( wxNumberFormatter::FromString("1234", &u) ); + CHECK( u == 1234 ); + + CHECK( wxNumberFormatter::FromString("1,234", &u) ); + CHECK( u == 1234 ); + + CHECK( wxNumberFormatter::FromString("12,345", &u) ); + CHECK( u == 12345 ); + + CHECK( wxNumberFormatter::FromString("123,456", &u) ); + CHECK( u == 123456 ); + + CHECK( wxNumberFormatter::FromString("1,234,567", &u) ); + CHECK( u == 1234567 ); + + CHECK( wxNumberFormatter::FromString("9223372036854775807", &u) ); + CHECK( u == static_cast(wxINT64_MAX) ); + + CHECK( wxNumberFormatter::FromString("9223372036854775808", &u) ); + CHECK( u == static_cast(wxINT64_MAX) + 1 ); + + CHECK( wxNumberFormatter::FromString("18446744073709551615", &u) ); + CHECK( u == wxUINT64_MAX ); + + CHECK( !wxNumberFormatter::FromString("18446744073709551616", &u) ); +} + TEST_CASE_METHOD(NumFormatterTestCase, "NumFormatter::DoubleFromString", "[numformatter]") { if ( !CanRunTest() )