From: Francesco Montorsi Date: Fri, 20 Mar 2009 14:50:06 +0000 (+0000) Subject: add the ToCLong, ToCULong and ToCDouble functions, with docs and test units X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/529e491ce038ad3a3aef4f3247ff7e16767d9734 add the ToCLong, ToCULong and ToCDouble functions, with docs and test units git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59645 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/string.h b/include/wx/string.h index bdba1d23cf..4a2dab67b3 100644 --- a/include/wx/string.h +++ b/include/wx/string.h @@ -2202,25 +2202,34 @@ public: // check if the string contents matches a mask containing '*' and '?' bool Matches(const wxString& mask) const; - // conversion to numbers: all functions return true only if the whole - // string is a number and put the value of this number into the pointer - // provided, the base is the numeric base in which the conversion should be - // done and must be comprised between 2 and 36 or be 0 in which case the - // standard C rules apply (leading '0' => octal, "0x" => hex) - // convert to a signed integer - bool ToLong(long *val, int base = 10) const; - // convert to an unsigned integer - bool ToULong(unsigned long *val, int base = 10) const; - // convert to wxLongLong + // conversion to numbers: all functions return true only if the whole + // string is a number and put the value of this number into the pointer + // provided, the base is the numeric base in which the conversion should be + // done and must be comprised between 2 and 36 or be 0 in which case the + // standard C rules apply (leading '0' => octal, "0x" => hex) + // convert to a signed integer + bool ToLong(long *val, int base = 10) const; + // convert to an unsigned integer + bool ToULong(unsigned long *val, int base = 10) const; + // convert to wxLongLong #if defined(wxLongLong_t) - bool ToLongLong(wxLongLong_t *val, int base = 10) const; - // convert to wxULongLong - bool ToULongLong(wxULongLong_t *val, int base = 10) const; + bool ToLongLong(wxLongLong_t *val, int base = 10) const; + // convert to wxULongLong + bool ToULongLong(wxULongLong_t *val, int base = 10) const; #endif // wxLongLong_t - // convert to a double - bool ToDouble(double *val) const; - - + // convert to a double + bool ToDouble(double *val) const; + +#if wxUSE_XLOCALE + // conversions to numbers using C locale + // convert to a signed integer + bool ToCLong(long *val, int base = 10) const; + // convert to an unsigned integer + bool ToCULong(unsigned long *val, int base = 10) const; + // convert to a double + bool ToCDouble(double *val) const; +#endif + #ifndef wxNEEDS_WXSTRING_PRINTF_MIXIN // formatted input/output // as sprintf(), returns the number of characters written or < 0 on error diff --git a/interface/wx/string.h b/interface/wx/string.h index fe98211313..3056895ba7 100644 --- a/interface/wx/string.h +++ b/interface/wx/string.h @@ -894,21 +894,43 @@ public: //@{ /** - Attempts to convert the string to a floating point number. Returns @true on - success (the number is stored in the location pointed to by @e val) or @false - if the string does not represent such number (the value of @a val is not - modified in this case). + Attempts to convert the string to a floating point number. + + Returns @true on success (the number is stored in the location pointed to by + @a val) or @false if the string does not represent such number (the value of + @a val is not modified in this case). + + Note that unlike ToCDouble() this function uses a localized version of + @c wxStrtod() and thus needs as decimal point (and thousands separator) the + locale-specific decimal point. Thus you should use this function only when + you are sure that this string contains a floating point number formatted with + the rules of the locale currently in use (see wxLocale). + + Refer to the docs of the standard function @c strtod() for more details about + the supported syntax. - @see ToLong(), ToULong() + @see ToCDouble(), ToLong(), ToULong() */ bool ToDouble(double* val) const; /** - Attempts to convert the string to a signed integer in base @e base. Returns - @true on success in which case the number is stored in the location + Works like ToDouble() but unlike it this function expects the floating point + number to be formatted always with the rules dictated by the "C" locale + (in particular, the decimal point must be a dot), independently from the + current application-wide locale (see wxLocale). + + @see ToDouble(), ToLong(), ToULong() + */ + bool ToCDouble(double* val) const; + + /** + Attempts to convert the string to a signed integer in base @a base. + + Returns @true on success in which case the number is stored in the location pointed to by @a val or @false if the string does not represent a valid number in the given base (the value of @a val is not modified in this case). + The value of @a base must be comprised between 2 and 36, inclusive, or be a special value 0 which means that the usual rules of @c C numbers are applied: if the number starts with @c 0x it is considered to be in base @@ -916,14 +938,31 @@ public: that you may not want to specify the base 0 if you are parsing the numbers which may have leading zeroes as they can yield unexpected (to the user not familiar with C) results. + + Note that unlike ToCLong() this function uses a localized version of + @c wxStrtol(). Thus you should use this function only when you are sure + that this string contains an integer number formatted with + the rules of the locale currently in use (see wxLocale). + + Refer to the docs of the standard function @c strtol() for more details about + the supported syntax. - @see ToDouble(), ToULong() + @see ToCDouble(), ToDouble(), ToULong() */ bool ToLong(long* val, int base = 10) const; /** - This is exactly the same as ToLong() but works with 64 - bit integer numbers. + Works like ToLong() but unlike it this function expects the integer + number to be formatted always with the rules dictated by the "C" locale, + independently from the current application-wide locale (see wxLocale). + + @see ToDouble(), ToLong(), ToULong() + */ + bool ToCLong(long* val, int base = 10) const; + + /** + This is exactly the same as ToLong() but works with 64 bit integer numbers. + Notice that currently it doesn't work (always returns @false) if parsing of 64 bit numbers is not supported by the underlying C run-time library. Compilers with C99 support and Microsoft Visual C++ version 7 and higher do support this. @@ -933,7 +972,8 @@ public: bool ToLongLong(wxLongLong_t* val, int base = 10) const; /** - Attempts to convert the string to an unsigned integer in base @e base. + Attempts to convert the string to an unsigned integer in base @a base. + Returns @true on success in which case the number is stored in the location pointed to by @a val or @false if the string does not represent a valid number in the given base (the value of @a val is not @@ -943,12 +983,22 @@ public: @c strtoul() and so it simply converts negative numbers to unsigned representation instead of rejecting them (e.g. -1 is returned as @c ULONG_MAX). - See ToLong() for the more detailed description of the @a base parameter. + See ToLong() for the more detailed description of the @a base parameter + (and of the locale-specific behaviour of this function). - @see ToDouble(), ToLong() + @see ToCULong(), ToDouble(), ToLong() */ bool ToULong(unsigned long* val, int base = 10) const; + /** + Works like ToULong() but unlike it this function expects the integer + number to be formatted always with the rules dictated by the "C" locale, + independently from the current application-wide locale (see wxLocale). + + @see ToDouble(), ToLong(), ToULong() + */ + bool ToCULong(unsigned long* val, int base = 10) const; + /** This is exactly the same as ToULong() but works with 64 bit integer numbers. diff --git a/src/common/string.cpp b/src/common/string.cpp index e07b7eea8f..ed7a6a8670 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -37,6 +37,7 @@ #include "wx/hashmap.h" #include "wx/vector.h" +#include "wx/xlocale.h" // string handling functions used by wxString: #if wxUSE_UNICODE_UTF8 @@ -1644,64 +1645,105 @@ int wxString::Find(wxUniChar ch, bool bFromEnd) const #define DO_IF_NOT_WINCE(x) #endif -#define WX_STRING_TO_INT_TYPE(out, base, func, T) \ - wxCHECK_MSG( out, false, _T("NULL output pointer") ); \ - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); \ - \ +#define WX_STRING_TO_X_TYPE_START \ + wxCHECK_MSG( pVal, false, _T("NULL output pointer") ); \ DO_IF_NOT_WINCE( errno = 0; ) \ - \ const wxStringCharType *start = wx_str(); \ - wxStringCharType *end; \ - T val = func(start, &end, base); \ - \ + wxStringCharType *end; + +#define WX_STRING_TO_X_TYPE_END \ /* return true only if scan was stopped by the terminating NUL and */ \ /* if the string was not empty to start with and no under/overflow */ \ /* occurred: */ \ if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) \ return false; \ - *out = val; \ - return true + *pVal = val; \ + return true; bool wxString::ToLong(long *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtol, long); + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + + WX_STRING_TO_X_TYPE_START + long val = wxStrtol(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToULong(unsigned long *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoul, unsigned long); + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + + WX_STRING_TO_X_TYPE_START + unsigned long val = wxStrtoul(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToLongLong(wxLongLong_t *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoll, wxLongLong_t); + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + + WX_STRING_TO_X_TYPE_START + wxLongLong_t val = wxStrtoll(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToULongLong(wxULongLong_t *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoull, wxULongLong_t); + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + + WX_STRING_TO_X_TYPE_START + wxULongLong_t val = wxStrtoull(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToDouble(double *pVal) const { - wxCHECK_MSG( pVal, false, _T("NULL output pointer") ); + WX_STRING_TO_X_TYPE_START + double val = wxStrtod(start, &end); + WX_STRING_TO_X_TYPE_END +} - DO_IF_NOT_WINCE( errno = 0; ) +#if wxUSE_XLOCALE - const wxChar *start = c_str(); - wxChar *end; - double val = wxStrtod(start, &end); +bool wxString::ToCLong(long *pVal, int base) const +{ + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); - // return true only if scan was stopped by the terminating NUL and if the - // string was not empty to start with and no under/overflow occurred - if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) - return false; + WX_STRING_TO_X_TYPE_START +#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE + long val = wxStrtol_lA(start, &end, base, wxCLocale); +#else + long val = wxStrtol_l(start, &end, base, wxCLocale); +#endif + WX_STRING_TO_X_TYPE_END +} - *pVal = val; +bool wxString::ToCULong(unsigned long *pVal, int base) const +{ + wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); - return true; + WX_STRING_TO_X_TYPE_START +#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE + unsigned long val = wxStrtoul_lA(start, &end, base, wxCLocale); +#else + unsigned long val = wxStrtoul_l(start, &end, base, wxCLocale); +#endif + WX_STRING_TO_X_TYPE_END } +bool wxString::ToCDouble(double *pVal) const +{ + WX_STRING_TO_X_TYPE_START +#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE + double val = wxStrtod_lA(start, &end, wxCLocale); +#else + double val = wxStrtod_l(start, &end, wxCLocale); +#endif + WX_STRING_TO_X_TYPE_END +} + +#endif // wxUSE_XLOCALE + // --------------------------------------------------------------------------- // formatted output // --------------------------------------------------------------------------- diff --git a/tests/strings/strings.cpp b/tests/strings/strings.cpp index 01c15182e7..b29747338a 100644 --- a/tests/strings/strings.cpp +++ b/tests/strings/strings.cpp @@ -588,6 +588,13 @@ void StringTestCase::ToLong() if ( ld.flags & (Number_LongLong | Number_Unsigned) ) continue; + + // NOTE: unless you're using some exotic locale, ToCLong and ToLong + // should behave the same for our test data set: + + CPPUNIT_ASSERT_EQUAL( ld.IsOk(), wxString(ld.str).ToCLong(&l) ); + if ( ld.IsOk() ) + CPPUNIT_ASSERT_EQUAL( ld.LValue(), l ); CPPUNIT_ASSERT_EQUAL( ld.IsOk(), wxString(ld.str).ToLong(&l) ); if ( ld.IsOk() ) @@ -605,6 +612,13 @@ void StringTestCase::ToULong() if ( ld.flags & (Number_LongLong | Number_Signed) ) continue; + // NOTE: unless you're using some exotic locale, ToCLong and ToLong + // should behave the same for our test data set: + + CPPUNIT_ASSERT_EQUAL( ld.IsOk(), wxString(ld.str).ToCULong(&ul) ); + if ( ld.IsOk() ) + CPPUNIT_ASSERT_EQUAL( ld.ULValue(), ul ); + CPPUNIT_ASSERT_EQUAL( ld.IsOk(), wxString(ld.str).ToULong(&ul) ); if ( ld.IsOk() ) CPPUNIT_ASSERT_EQUAL( ld.ULValue(), ul ); @@ -667,20 +681,59 @@ void StringTestCase::ToDouble() { _T("12345"), 12345, true }, { _T("-1"), -1, true }, { _T("--1"), 0, false }, + { _T("-3E-5"), -3E-5, true }, + { _T("-3E-abcde5"), 0, false }, }; - // we need to use decimal point, not comma or whatever is its value for the - // current locale - wxSetlocale(LC_ALL, "C"); + // test ToCDouble() first: size_t n; for ( n = 0; n < WXSIZEOF(doubleData); n++ ) { const ToDoubleData& ld = doubleData[n]; + CPPUNIT_ASSERT_EQUAL( ld.ok, wxString(ld.str).ToCDouble(&d) ); + if ( ld.ok ) + CPPUNIT_ASSERT_EQUAL( ld.value, d ); + } + + + // test ToDouble() now: + // NOTE: for the test to be reliable, we need to set the locale explicitely + // so that we know the decimal point character to use + + if (!wxLocale::IsAvailable(wxLANGUAGE_FRENCH)) + return; // you should have french support installed to continue this test! + + wxLocale *locale = new wxLocale; + + // don't load default catalog, it may be unavailable: + CPPUNIT_ASSERT( locale->Init(wxLANGUAGE_FRENCH, wxLOCALE_CONV_ENCODING) ); + + static const struct ToDoubleData doubleData2[] = + { + { _T("1"), 1, true }, + { _T("1,23"), 1.23, true }, + { _T(",1"), .1, true }, + { _T("1,"), 1, true }, + { _T("1,,"), 0, false }, + { _T("0"), 0, true }, + { _T("a"), 0, false }, + { _T("12345"), 12345, true }, + { _T("-1"), -1, true }, + { _T("--1"), 0, false }, + { _T("-3E-5"), -3E-5, true }, + { _T("-3E-abcde5"), 0, false }, + }; + + for ( n = 0; n < WXSIZEOF(doubleData2); n++ ) + { + const ToDoubleData& ld = doubleData2[n]; CPPUNIT_ASSERT_EQUAL( ld.ok, wxString(ld.str).ToDouble(&d) ); if ( ld.ok ) CPPUNIT_ASSERT_EQUAL( ld.value, d ); } + + delete locale; } void StringTestCase::StringBuf()