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
This commit is contained in:
Francesco Montorsi
2009-03-20 14:50:06 +00:00
parent f1c40652a0
commit 529e491ce0
4 changed files with 215 additions and 61 deletions

View File

@@ -2202,25 +2202,34 @@ public:
// check if the string contents matches a mask containing '*' and '?' // check if the string contents matches a mask containing '*' and '?'
bool Matches(const wxString& mask) const; bool Matches(const wxString& mask) const;
// conversion to numbers: all functions return true only if the whole // 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 // 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 // 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 // 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) // standard C rules apply (leading '0' => octal, "0x" => hex)
// convert to a signed integer // convert to a signed integer
bool ToLong(long *val, int base = 10) const; bool ToLong(long *val, int base = 10) const;
// convert to an unsigned integer // convert to an unsigned integer
bool ToULong(unsigned long *val, int base = 10) const; bool ToULong(unsigned long *val, int base = 10) const;
// convert to wxLongLong // convert to wxLongLong
#if defined(wxLongLong_t) #if defined(wxLongLong_t)
bool ToLongLong(wxLongLong_t *val, int base = 10) const; bool ToLongLong(wxLongLong_t *val, int base = 10) const;
// convert to wxULongLong // convert to wxULongLong
bool ToULongLong(wxULongLong_t *val, int base = 10) const; bool ToULongLong(wxULongLong_t *val, int base = 10) const;
#endif // wxLongLong_t #endif // wxLongLong_t
// convert to a double // convert to a double
bool ToDouble(double *val) const; 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 #ifndef wxNEEDS_WXSTRING_PRINTF_MIXIN
// formatted input/output // formatted input/output
// as sprintf(), returns the number of characters written or < 0 on error // as sprintf(), returns the number of characters written or < 0 on error

View File

@@ -894,21 +894,43 @@ public:
//@{ //@{
/** /**
Attempts to convert the string to a floating point number. Returns @true on Attempts to convert the string to a floating point number.
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 Returns @true on success (the number is stored in the location pointed to by
modified in this case). @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; bool ToDouble(double* val) const;
/** /**
Attempts to convert the string to a signed integer in base @e base. Returns Works like ToDouble() but unlike it this function expects the floating point
@true on success in which case the number is stored in the location 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 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 valid number in the given base (the value of @a val is not modified
in this case). in this case).
The value of @a base must be comprised between 2 and 36, inclusive, or 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 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 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 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 which may have leading zeroes as they can yield unexpected (to the user not
familiar with C) results. 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; bool ToLong(long* val, int base = 10) const;
/** /**
This is exactly the same as ToLong() but works with 64 Works like ToLong() but unlike it this function expects the integer
bit integer numbers. 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 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 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. 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; 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 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 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 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 @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). 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; 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 This is exactly the same as ToULong() but works with 64
bit integer numbers. bit integer numbers.

View File

@@ -37,6 +37,7 @@
#include "wx/hashmap.h" #include "wx/hashmap.h"
#include "wx/vector.h" #include "wx/vector.h"
#include "wx/xlocale.h"
// string handling functions used by wxString: // string handling functions used by wxString:
#if wxUSE_UNICODE_UTF8 #if wxUSE_UNICODE_UTF8
@@ -1644,64 +1645,105 @@ int wxString::Find(wxUniChar ch, bool bFromEnd) const
#define DO_IF_NOT_WINCE(x) #define DO_IF_NOT_WINCE(x)
#endif #endif
#define WX_STRING_TO_INT_TYPE(out, base, func, T) \ #define WX_STRING_TO_X_TYPE_START \
wxCHECK_MSG( out, false, _T("NULL output pointer") ); \ wxCHECK_MSG( pVal, false, _T("NULL output pointer") ); \
wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); \
\
DO_IF_NOT_WINCE( errno = 0; ) \ DO_IF_NOT_WINCE( errno = 0; ) \
\
const wxStringCharType *start = wx_str(); \ const wxStringCharType *start = wx_str(); \
wxStringCharType *end; \ wxStringCharType *end;
T val = func(start, &end, base); \
\ #define WX_STRING_TO_X_TYPE_END \
/* return true only if scan was stopped by the terminating NUL and */ \ /* 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 */ \ /* if the string was not empty to start with and no under/overflow */ \
/* occurred: */ \ /* occurred: */ \
if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) \ if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) \
return false; \ return false; \
*out = val; \ *pVal = val; \
return true return true;
bool wxString::ToLong(long *pVal, int base) const 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 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 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 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 bool wxString::ToDouble(double *pVal) const
{ {
wxCHECK_MSG( pVal, false, _T("NULL output pointer") ); WX_STRING_TO_X_TYPE_START
DO_IF_NOT_WINCE( errno = 0; )
const wxChar *start = c_str();
wxChar *end;
double val = wxStrtod(start, &end); double val = wxStrtod(start, &end);
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;
*pVal = val;
return true;
} }
#if wxUSE_XLOCALE
bool wxString::ToCLong(long *pVal, int base) const
{
wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
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
}
bool wxString::ToCULong(unsigned long *pVal, int base) const
{
wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") );
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 // formatted output
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@@ -588,6 +588,13 @@ void StringTestCase::ToLong()
if ( ld.flags & (Number_LongLong | Number_Unsigned) ) if ( ld.flags & (Number_LongLong | Number_Unsigned) )
continue; 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) ); CPPUNIT_ASSERT_EQUAL( ld.IsOk(), wxString(ld.str).ToLong(&l) );
if ( ld.IsOk() ) if ( ld.IsOk() )
@@ -605,6 +612,13 @@ void StringTestCase::ToULong()
if ( ld.flags & (Number_LongLong | Number_Signed) ) if ( ld.flags & (Number_LongLong | Number_Signed) )
continue; 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) ); CPPUNIT_ASSERT_EQUAL( ld.IsOk(), wxString(ld.str).ToULong(&ul) );
if ( ld.IsOk() ) if ( ld.IsOk() )
CPPUNIT_ASSERT_EQUAL( ld.ULValue(), ul ); CPPUNIT_ASSERT_EQUAL( ld.ULValue(), ul );
@@ -667,20 +681,59 @@ void StringTestCase::ToDouble()
{ _T("12345"), 12345, true }, { _T("12345"), 12345, true },
{ _T("-1"), -1, true }, { _T("-1"), -1, true },
{ _T("--1"), 0, false }, { _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 // test ToCDouble() first:
// current locale
wxSetlocale(LC_ALL, "C");
size_t n; size_t n;
for ( n = 0; n < WXSIZEOF(doubleData); n++ ) for ( n = 0; n < WXSIZEOF(doubleData); n++ )
{ {
const ToDoubleData& ld = 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) ); CPPUNIT_ASSERT_EQUAL( ld.ok, wxString(ld.str).ToDouble(&d) );
if ( ld.ok ) if ( ld.ok )
CPPUNIT_ASSERT_EQUAL( ld.value, d ); CPPUNIT_ASSERT_EQUAL( ld.value, d );
} }
delete locale;
} }
void StringTestCase::StringBuf() void StringTestCase::StringBuf()