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,24 +2202,33 @@ 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

View File

@@ -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.
@see ToLong(), ToULong()
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 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
@@ -917,13 +939,30 @@ public:
which may have leading zeroes as they can yield unexpected (to the user not
familiar with C) results.
@see ToDouble(), ToULong()
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 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.

View File

@@ -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") );
DO_IF_NOT_WINCE( errno = 0; )
const wxChar *start = c_str();
wxChar *end;
WX_STRING_TO_X_TYPE_START
double val = wxStrtod(start, &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;
WX_STRING_TO_X_TYPE_END
}
#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
// ---------------------------------------------------------------------------

View File

@@ -589,6 +589,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() )
CPPUNIT_ASSERT_EQUAL( ld.LValue(), l );
@@ -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()