Fixes #5417. Much better performance on casts to integral types.

Adds more tests for overflow detection.
Workaround for bugs of vc8 (lexical_cast_vc8_bug_test.cpp now passes)
Fixes some inspection errors.

[SVN r72056]
This commit is contained in:
Antony Polukhin
2011-05-20 17:11:53 +00:00
parent 95360b5df6
commit dc9b364d6f
4 changed files with 296 additions and 44 deletions

View File

@ -577,6 +577,84 @@ namespace boost
}
}
namespace detail // lcast_ret_unsigned
{
template<class Traits, class T, class CharT>
inline bool lcast_ret_unsigned(T& value, const CharT* const begin, const CharT* end)
{
#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
BOOST_STATIC_ASSERT(!std::numeric_limits<T>::is_signed);
#endif
typedef typename Traits::int_type int_type;
CharT const czero = lcast_char_constants<CharT>::zero;
--end;
value = 0;
if ( *end < czero || *end >= czero + 10 || begin > end)
return false;
value = *end - czero;
--end;
T multiplier = 1;
#ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
// TODO: use BOOST_NO_STD_LOCALE
std::locale loc;
typedef std::numpunct<CharT> numpunct;
numpunct const& np = BOOST_USE_FACET(numpunct, loc);
std::string const& grouping = np.grouping();
std::string::size_type const grouping_size = grouping.size();
/* According to [22.2.2.1.2] of Programming languages — C++ we MUST check for correct grouping */
if (grouping_size)
{
unsigned char current_grouping = 0;
CharT const thousands_sep = grouping_size ? np.thousands_sep() : 0;
char remained = grouping[current_grouping] - 1;
for(;end>=begin; --end)
{
if (remained) {
T const new_sub_value = multiplier * 10 * (*end - czero);
if (*end < czero || *end >= czero + 10
/* detecting overflow */
|| new_sub_value/10 != multiplier * (*end - czero)
|| static_cast<T>((std::numeric_limits<T>::max)()-new_sub_value) < value
)
return false;
value += new_sub_value;
multiplier *= 10;
--remained;
} else {
if ( !Traits::eq(*end, thousands_sep) || begin == end ) return false;
if (current_grouping < grouping_size-1 ) ++current_grouping;
remained = grouping[current_grouping];
}
}
} else
#endif
{
while ( begin <= end )
{
T const new_sub_value = multiplier * 10 * (*end - czero);
if (*end < czero || *end >= czero + 10
/* detecting overflow */
|| new_sub_value/10 != multiplier * (*end - czero)
|| static_cast<T>((std::numeric_limits<T>::max)()-new_sub_value) < value
)
return false;
value += new_sub_value;
multiplier *= 10;
--end;
}
}
return true;
}
}
namespace detail // stream wrapper for handling lexical conversions
{
template<typename Target, typename Source, typename Traits>
@ -762,8 +840,130 @@ namespace boost
bool operator<<(double);
bool operator<<(long double);
private:
template <typename Type>
bool input_operator_helper_unsigned(Type& output)
{
CharT const minus = lcast_char_constants<CharT>::minus;
bool const has_minus = Traits::eq(minus,*start);
bool const succeed = lcast_ret_unsigned<Traits>(output, has_minus ? start+1 : start, finish);
#if (defined _MSC_VER)
# pragma warning( push )
// C4146: unary minus operator applied to unsigned type, result still unsigned
# pragma warning( disable : 4146 )
#elif defined( __BORLANDC__ )
# pragma option push -w-8041
#endif
if (has_minus) output = static_cast<Type>(-output);
#if (defined _MSC_VER)
# pragma warning( pop )
#elif defined( __BORLANDC__ )
# pragma option pop
#endif
return succeed;
}
template <typename Type>
bool input_operator_helper_signed(Type& output)
{
CharT const minus = lcast_char_constants<CharT>::minus;
typedef BOOST_DEDUCED_TYPENAME make_unsigned<Type>::type utype;
utype out_tmp =0;
bool const has_minus = Traits::eq(minus,*start);
bool succeed = lcast_ret_unsigned<Traits>(out_tmp, has_minus ? start+1 : start, finish);
if (has_minus) {
#if (defined _MSC_VER)
# pragma warning( push )
// C4146: unary minus operator applied to unsigned type, result still unsigned
# pragma warning( disable : 4146 )
#elif defined( __BORLANDC__ )
# pragma option push -w-8041
#endif
utype const comp_val = static_cast<utype>(-(std::numeric_limits<Type>::min)());
succeed = succeed && out_tmp<=comp_val;
output = -out_tmp;
#if (defined _MSC_VER)
# pragma warning( pop )
#elif defined( __BORLANDC__ )
# pragma option pop
#endif
} else {
utype const comp_val = static_cast<utype>((std::numeric_limits<Type>::max)());
succeed = succeed && out_tmp<=comp_val;
output = out_tmp;
}
return succeed;
}
public: // input
bool operator>>(unsigned short& output)
{
return input_operator_helper_unsigned(output);
}
bool operator>>(unsigned int& output)
{
return input_operator_helper_unsigned(output);
}
bool operator>>(unsigned long int& output)
{
return input_operator_helper_unsigned(output);
}
bool operator>>(short& output)
{
return input_operator_helper_signed(output);
}
bool operator>>(int& output)
{
return input_operator_helper_signed(output);
}
bool operator>>(long int& output)
{
return input_operator_helper_signed(output);
}
#if defined(BOOST_HAS_LONG_LONG)
bool operator>>( boost::ulong_long_type& output)
{
return input_operator_helper_unsigned(output);
}
bool operator>>(boost::long_long_type& output)
{
return input_operator_helper_signed(output);
}
#elif defined(BOOST_HAS_MS_INT64)
bool operator>>(unsigned __int64& output)
{
return input_operator_helper_unsigned(output);
}
bool operator>>(__int64& output)
{
return input_operator_helper_signed(output);
}
#endif
bool operator>>(bool& output)
{
output = (start[0] == lcast_char_constants<CharT>::zero + 1);
return finish-start==1
&& (
start[0] == lcast_char_constants<CharT>::zero
|| start[0] == lcast_char_constants<CharT>::zero + 1
);
}
// Generic istream-based algorithm.
// lcast_streambuf_for_target<InputStreamable>::value is true.
template<typename InputStreamable>
@ -1059,23 +1259,17 @@ namespace boost
template<class Target>
struct lcast_streambuf_for_target
{
BOOST_STATIC_CONSTANT(bool, value = true);
BOOST_STATIC_CONSTANT(bool, value =
(
::boost::type_traits::ice_or<
::boost::type_traits::ice_not< is_integral<Target>::value >::value,
is_same<Target, signed char>::value,
is_same<Target, unsigned char>::value
>::value
)
);
};
template<>
struct lcast_streambuf_for_target<char>
{
BOOST_STATIC_CONSTANT(bool, value = false);
};
#if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T)
template<>
struct lcast_streambuf_for_target<wchar_t>
{
BOOST_STATIC_CONSTANT(bool, value = false);
};
#endif
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
template<class Traits, class Alloc>
struct lcast_streambuf_for_target<
@ -1328,7 +1522,7 @@ namespace boost
> Converter ;
return Converter::convert(arg);
} catch(...) {
} catch( ::boost::numeric::bad_numeric_cast const& ) {
BOOST_LCAST_THROW_BAD_CAST(Source, Target);
}
}
@ -1354,7 +1548,7 @@ namespace boost
} else {
return Converter::convert(arg);
}
} catch(...) {
} catch( ::boost::numeric::bad_numeric_cast const& ) {
BOOST_LCAST_THROW_BAD_CAST(Source, Target);
}
}

View File

@ -267,7 +267,8 @@ Eliminate an overhead of <code>std::locale</code> if your program runs in the "C
<h2><a name="changes">Changes</a></h2>
<h3>May 2011:</h3>
<ul type="square">
<li>Better performance for conversions from arithmetic type to arithmetic type.</li>
<li>Better performance and less memory usage for conversions to arithmetic types.</li>
<li>Better performance and less memory usage for conversions from arithmetic type to arithmetic type.</li>
<li>Directly construct <code>Target</code> from <code>Source</code> on some conversions (like conversions from string to string, from char array to string, from char to char and others).</li>
</ul>
<h3>August, October 2006:</h3>
@ -317,7 +318,7 @@ Eliminate an overhead of <code>std::locale</code> if your program runs in the "C
<div align="right"><small><i>Copyright &copy; Antony Polukhin, 2011</i></small></div>
<div align="right"><small><i>
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at <a href="/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)</i></small>
file LICENSE_1_0.txt or copy at <a href="../../LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)</i></small>
</div>
</body>
</html>

View File

@ -4,6 +4,7 @@
//
// Copyright Terje Sletteb and Kevlin Henney, 2005.
// Copyright Alexander Nasonov, 2006.
// Copyright Antony Polukhin, 2011.
//
// Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
@ -32,6 +33,7 @@
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/type_traits/integral_promotion.hpp>
#include <string>
#include <memory>
@ -235,6 +237,14 @@ void test_conversion_to_bool()
BOOST_CHECK_EQUAL(false, lexical_cast<bool>("0"));
BOOST_CHECK_EQUAL(true, lexical_cast<bool>(std::string("1")));
BOOST_CHECK_EQUAL(false, lexical_cast<bool>(std::string("0")));
BOOST_CHECK_THROW(lexical_cast<bool>(1.0001L), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<bool>(2), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<bool>(2u), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<bool>(-1), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<bool>(-2), bad_lexical_cast);
BOOST_CHECK_THROW(
lexical_cast<bool>(std::string("")), bad_lexical_cast);
BOOST_CHECK_THROW(
@ -564,25 +574,46 @@ void test_conversion_from_string_to_integral(CharT)
BOOST_CHECK_EQUAL(lexical_cast<T>(s), min_val);
if(limits::is_signed)
{
#if defined(BOOST_MSVC) && BOOST_MSVC == 1400
// VC++ 8.0 bug, see libs/conversion/test/lexical_cast_vc8_bug_test.cpp
if(sizeof(T) < sizeof(boost::intmax_t))
#endif
{
BOOST_CHECK_THROW(lexical_cast<T>(s + zero), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<T>(s + nine), bad_lexical_cast);
}
BOOST_CHECK_THROW(lexical_cast<T>(s + zero), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<T>(s + nine), bad_lexical_cast);
}
s = to_str<CharT>(max_val);
BOOST_CHECK_EQUAL(lexical_cast<T>(s), max_val);
#if defined(BOOST_MSVC) && BOOST_MSVC == 1400
// VC++ 8.0 bug, see libs/conversion/test/lexical_cast_vc8_bug_test.cpp
if(sizeof(T) != sizeof(boost::intmax_t))
#endif
{
BOOST_CHECK_THROW(lexical_cast<T>(s + zero), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<T>(s + nine), bad_lexical_cast);
s = to_str<CharT>(max_val);
for (int i =1; i <=10; ++i) {
s[s.size()-1] += 1;
BOOST_CHECK_THROW(lexical_cast<T>( s ), bad_lexical_cast);
}
s = to_str<CharT>(max_val);
std::locale loc;
typedef std::numpunct<char> numpunct;
if ( BOOST_USE_FACET(numpunct, loc).grouping().empty() ) {
// Following tests work well for locale C
BOOST_CHECK_EQUAL(lexical_cast<T>(to_str<CharT>(0)+s), max_val);
BOOST_CHECK_EQUAL(lexical_cast<T>(to_str<CharT>(0)+to_str<CharT>(0)+s), max_val);
BOOST_CHECK_EQUAL(lexical_cast<T>(to_str<CharT>(0)+to_str<CharT>(0)+to_str<CharT>(0)+s), max_val);
}
for (int i =1; i <=256; ++i) {
BOOST_CHECK_THROW(lexical_cast<T>( to_str<CharT>(i)+s ), bad_lexical_cast);
}
typedef BOOST_DEDUCED_TYPENAME boost::integral_promotion<T>::type promoted;
if ( !(boost::is_same<T, promoted>::value) )
{
promoted prom = max_val;
s = to_str<CharT>(max_val);
for (int i =1; i <=256; ++i) {
BOOST_CHECK_THROW(lexical_cast<T>( to_str<CharT>(prom+i) ), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<T>( to_str<CharT>(i)+s ), bad_lexical_cast);
}
}
}
if(limits::digits <= 16 && lcast_test_small_integral_types_completely)
@ -627,6 +658,18 @@ void test_conversion_from_string_to_integral(CharT)
template<class T>
void test_conversion_from_to_integral_for_locale()
{
std::locale current_locale;
typedef std::numpunct<char> numpunct;
numpunct const& np = BOOST_USE_FACET(numpunct, current_locale);
if ( !np.grouping().empty() )
{
BOOST_CHECK_THROW(
lexical_cast<T>( std::string("100") + np.thousands_sep() + np.thousands_sep() + "0" )
, bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<T>( std::string("100") + np.thousands_sep() ), bad_lexical_cast);
BOOST_CHECK_THROW(lexical_cast<T>( np.thousands_sep() + std::string("100") ), bad_lexical_cast);
}
test_conversion_from_integral_to_integral<T>();
test_conversion_from_integral_to_string<T>('0');
test_conversion_from_string_to_integral<T>('0');
@ -773,18 +816,6 @@ void test_conversion_from_to_uintmax_t()
test_conversion_from_to_integral<boost::uintmax_t>();
}
#if defined(BOOST_HAS_LONG_LONG)
void test_conversion_from_to_longlong()
{
test_conversion_from_to_integral<boost::long_long_type>();
}
void test_conversion_from_to_ulonglong()
{
test_conversion_from_to_integral<boost::ulong_long_type>();
}
void test_conversion_from_to_float()
{
test_conversion_from_to_float<float>();
@ -798,7 +829,19 @@ void test_conversion_from_to_long_double()
test_conversion_from_to_float<long double>();
}
#elif defined(LCAST_TEST_LONGLONG)
#if defined(BOOST_HAS_LONG_LONG)
void test_conversion_from_to_longlong()
{
test_conversion_from_to_integral<boost::long_long_type>();
}
void test_conversion_from_to_ulonglong()
{
test_conversion_from_to_integral<boost::ulong_long_type>();
}
#elif defined(BOOST_HAS_MS_INT64)
void test_conversion_from_to_longlong()
{

View File

@ -1,3 +1,17 @@
// Unit test for boost::lexical_cast.
//
// See http://www.boost.org for most recent version, including documentation.
//
// Copyright Alexander Nasonov, 2007.
//
// Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt).
//
// This tests now must pass on vc8, because lexical_cast
// implementation has changed and it does not use stringstream for casts
// to integral types
#include <boost/config.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/cstdint.hpp>