Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r72056 - in trunk: boost libs/conversion libs/conversion/test
From: antoshkka_at_[hidden]
Date: 2011-05-20 13:11:55


Author: apolukhin
Date: 2011-05-20 13:11:53 EDT (Fri, 20 May 2011)
New Revision: 72056
URL: http://svn.boost.org/trac/boost/changeset/72056

Log:
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.
Text files modified:
   trunk/boost/lexical_cast.hpp | 228 +++++++++++++++++++++++++++++++++++++--
   trunk/libs/conversion/lexical_cast.htm | 5
   trunk/libs/conversion/lexical_cast_test.cpp | 93 +++++++++++----
   trunk/libs/conversion/test/lexical_cast_vc8_bug_test.cpp | 14 ++
   4 files changed, 296 insertions(+), 44 deletions(-)

Modified: trunk/boost/lexical_cast.hpp
==============================================================================
--- trunk/boost/lexical_cast.hpp (original)
+++ trunk/boost/lexical_cast.hpp 2011-05-20 13:11:53 EDT (Fri, 20 May 2011)
@@ -577,6 +577,84 @@
         }
     }
 
+ 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 @@
             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,22 +1259,16 @@
         template<class Target>
         struct lcast_streambuf_for_target
         {
- BOOST_STATIC_CONSTANT(bool, value = true);
- };
-
- 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);
+ 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
+ )
+ );
         };
-#endif
 
 #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
         template<class Traits, class Alloc>
@@ -1328,7 +1522,7 @@
> Converter ;
 
                     return Converter::convert(arg);
- } catch(...) {
+ } catch( ::boost::numeric::bad_numeric_cast const& ) {
                     BOOST_LCAST_THROW_BAD_CAST(Source, Target);
                 }
             }
@@ -1354,7 +1548,7 @@
                     } else {
                         return Converter::convert(arg);
                     }
- } catch(...) {
+ } catch( ::boost::numeric::bad_numeric_cast const& ) {
                     BOOST_LCAST_THROW_BAD_CAST(Source, Target);
                 }
             }

Modified: trunk/libs/conversion/lexical_cast.htm
==============================================================================
--- trunk/libs/conversion/lexical_cast.htm (original)
+++ trunk/libs/conversion/lexical_cast.htm 2011-05-20 13:11:53 EDT (Fri, 20 May 2011)
@@ -267,7 +267,8 @@
 <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 @@
 <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 http://www.boost.org/LICENSE_1_0.txt)</i></small>
+ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)</i></small>
 </div>
         </body>
 </html>

Modified: trunk/libs/conversion/lexical_cast_test.cpp
==============================================================================
--- trunk/libs/conversion/lexical_cast_test.cpp (original)
+++ trunk/libs/conversion/lexical_cast_test.cpp 2011-05-20 13:11:53 EDT (Fri, 20 May 2011)
@@ -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 @@
     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 @@
     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 @@
 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 @@
     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 @@
     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()
 {

Modified: trunk/libs/conversion/test/lexical_cast_vc8_bug_test.cpp
==============================================================================
--- trunk/libs/conversion/test/lexical_cast_vc8_bug_test.cpp (original)
+++ trunk/libs/conversion/test/lexical_cast_vc8_bug_test.cpp 2011-05-20 13:11:53 EDT (Fri, 20 May 2011)
@@ -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>


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk