From 95360b5df6959f691eefa12c0b6c84471e73b24c Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 15 May 2011 15:31:01 +0000 Subject: [PATCH] Fixes #5417. Fixes #4397 More tests (for conversions to float types, for conversions of negative values to unsigned integers) [SVN r71958] --- include/boost/lexical_cast.hpp | 282 +++++++++++++++++++++++++++++++-- lexical_cast.htm | 5 + lexical_cast_test.cpp | 83 +++++++++- 3 files changed, 356 insertions(+), 14 deletions(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index fdc5898..7a4a5ee 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -11,8 +11,8 @@ // enhanced with contributions from Terje Slettebo, // with additional fixes and suggestions from Gennaro Prota, // Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov, -// Alexander Nasonov and other Boosters -// when: November 2000, March 2003, June 2005, June 2006 +// Alexander Nasonov, Antony Polukhin and other Boosters +// when: November 2000, March 2003, June 2005, June 2006, March 2011 #include #include @@ -20,12 +20,18 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include +#include #include #include #include @@ -1161,23 +1167,272 @@ namespace boost #if (defined _MSC_VER) # pragma warning( pop ) #endif + + template + struct is_stdstring + { + BOOST_STATIC_CONSTANT(bool, value = false ); + }; + + template + struct is_stdstring< std::basic_string > + { + BOOST_STATIC_CONSTANT(bool, value = true ); + }; + + template + struct is_char_or_wchar + { +#ifndef BOOST_LCAST_NO_WCHAR_T + BOOST_STATIC_CONSTANT(bool, value = + ( + ::boost::type_traits::ice_or< + is_same< T, char >::value, + is_same< T, wchar_t >::value, + is_same< T, unsigned char >::value, + is_same< T, signed char >::value + >::value + ) + ); +#else + BOOST_STATIC_CONSTANT(bool, value = + ( + ::boost::type_traits::ice_or< + is_same< T, char >::value, + is_same< T, unsigned char >::value, + is_same< T, signed char >::value + >::value + ) + ); +#endif + }; + + template + struct is_arithmetic_and_not_xchars + { + BOOST_STATIC_CONSTANT(bool, value = + ( + ::boost::type_traits::ice_and< + is_arithmetic::value, + is_arithmetic::value, + ::boost::type_traits::ice_not< + detail::is_char_or_wchar::value + >::value, + ::boost::type_traits::ice_not< + detail::is_char_or_wchar::value + >::value + >::value + ) + ); + }; + + template + struct is_xchar_to_xchar + { + BOOST_STATIC_CONSTANT(bool, value = + ( + ::boost::type_traits::ice_and< + is_same::value, + is_char_or_wchar::value + >::value + ) + ); + }; + + template + struct is_char_array_to_stdstring + { + BOOST_STATIC_CONSTANT(bool, value = false ); + }; + + template + struct is_char_array_to_stdstring< std::basic_string, CharT* > + { + BOOST_STATIC_CONSTANT(bool, value = true ); + }; + + template + struct is_char_array_to_stdstring< std::basic_string, const CharT* > + { + BOOST_STATIC_CONSTANT(bool, value = true ); + }; + + template + struct lexical_cast_do_cast + { + static inline Target lexical_cast_impl(const Source &arg) + { + typedef typename detail::array_to_pointer_decay::type src; + + typedef typename detail::widest_char< + typename detail::stream_char::type + , typename detail::stream_char::type + >::type char_type; + + typedef detail::lcast_src_length lcast_src_length; + std::size_t const src_len = lcast_src_length::value; + char_type buf[src_len + 1]; + lcast_src_length::check_coverage(); + return detail::lexical_cast(arg, buf, src_len); + } + }; + + template + struct lexical_cast_copy + { + static inline Source lexical_cast_impl(const Source &arg) + { + return arg; + } + }; + + class precision_loss_error : public boost::numeric::bad_numeric_cast + { + public: + virtual const char * what() const throw() + { return "bad numeric conversion: precision loss error"; } + }; + + template + struct throw_on_precision_loss + { + typedef boost::numeric::Trunc Rounder; + typedef S source_type ; + + typedef typename mpl::if_< is_arithmetic,S,S const&>::type argument_type ; + + static source_type nearbyint ( argument_type s ) + { + source_type orig_div_round = s / Rounder::nearbyint(s); + + if ( (orig_div_round > 1 ? orig_div_round - 1 : 1 - orig_div_round) > std::numeric_limits::epsilon() ) + BOOST_THROW_EXCEPTION( precision_loss_error() ); + return s ; + } + + typedef typename Rounder::round_style round_style; + } ; + + template + struct lexical_cast_dynamic_num_not_ignoring_minus + { + static inline Target lexical_cast_impl(const Source &arg) + { + try{ + typedef boost::numeric::converter< + Target, + Source, + boost::numeric::conversion_traits, + boost::numeric::def_overflow_handler, + throw_on_precision_loss + > Converter ; + + return Converter::convert(arg); + } catch(...) { + BOOST_LCAST_THROW_BAD_CAST(Source, Target); + } + } + }; + + template + struct lexical_cast_dynamic_num_ignoring_minus + { + static inline Target lexical_cast_impl(const Source &arg) + { + try{ + typedef boost::numeric::converter< + Target, + Source, + boost::numeric::conversion_traits, + boost::numeric::def_overflow_handler, + throw_on_precision_loss + > Converter ; + + bool has_minus = ( arg < 0); + if ( has_minus ) { + return static_cast(-Converter::convert(-arg)); + } else { + return Converter::convert(arg); + } + } catch(...) { + BOOST_LCAST_THROW_BAD_CAST(Source, Target); + } + } + }; + + /* + * lexical_cast_dynamic_num follows the rules: + * 1) If Source can be converted to Target without precision loss and + * without overflows, then assign Source to Target and return + * + * 2) If Source is less than 0 and Target is an unsigned integer, + * then negate Source, check the requirements of rule 1) and if + * successful, assign static_casted Source to Target and return + * + * 3) Otherwise throw a bad_lexical_cast exception + * + * + * Rule 2) required because boost::lexical_cast has the behavior of + * stringstream, which uses the rules of scanf for conversions. And + * in the C99 standard for unsigned input value minus sign is + * optional, so if a negative number is read, no errors will arise + * and the result will be the two's complement. + */ + template + struct lexical_cast_dynamic_num + { + static inline Target lexical_cast_impl(const Source &arg) + { + typedef BOOST_DEDUCED_TYPENAME ::boost::mpl::if_c< + ::boost::type_traits::ice_and< + ::boost::type_traits::ice_or< + ::boost::is_signed::value, + ::boost::is_float::value + >::value, + ::boost::type_traits::ice_not< + is_same::value + >::value, + ::boost::type_traits::ice_not< + is_same::value + >::value, + ::boost::is_unsigned::value + >::value, + lexical_cast_dynamic_num_ignoring_minus, + lexical_cast_dynamic_num_not_ignoring_minus + >::type caster_type; + + return caster_type::lexical_cast_impl(arg); + } + }; } template inline Target lexical_cast(const Source &arg) { - typedef typename detail::array_to_pointer_decay::type src; + typedef BOOST_DEDUCED_TYPENAME detail::array_to_pointer_decay::type src; - typedef typename detail::widest_char< - typename detail::stream_char::type - , typename detail::stream_char::type - >::type char_type; + typedef BOOST_DEDUCED_TYPENAME ::boost::type_traits::ice_or< + detail::is_xchar_to_xchar::value, + detail::is_char_array_to_stdstring::value, + ::boost::type_traits::ice_and< + is_same::value, + detail::is_stdstring::value + >::value + > do_copy_type; - typedef detail::lcast_src_length lcast_src_length; - std::size_t const src_len = lcast_src_length::value; - char_type buf[src_len + 1]; - lcast_src_length::check_coverage(); - return detail::lexical_cast(arg, buf, src_len); + typedef BOOST_DEDUCED_TYPENAME detail::is_arithmetic_and_not_xchars do_copy_with_dynamic_check_type; + + typedef BOOST_DEDUCED_TYPENAME ::boost::mpl::if_c< + do_copy_type::value, + detail::lexical_cast_copy, + BOOST_DEDUCED_TYPENAME ::boost::mpl::if_c< + do_copy_with_dynamic_check_type::value, + detail::lexical_cast_dynamic_num, + detail::lexical_cast_do_cast + >::type + >::type caster_type; + + return caster_type::lexical_cast_impl(arg); } #else @@ -1205,7 +1460,8 @@ namespace boost } // Copyright Kevlin Henney, 2000-2005. -// Copyright Alexander Nasonov, 2006-2007. +// Copyright Alexander Nasonov, 2006-2010. +// Copyright Antony Polukhin, 2011. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at diff --git a/lexical_cast.htm b/lexical_cast.htm index 5457a33..268684e 100644 --- a/lexical_cast.htm +++ b/lexical_cast.htm @@ -265,6 +265,11 @@ Eliminate an overhead of std::locale if your program runs in the "C August 2006.

Changes

+

May 2011:

+
    +
  • Better performance for conversions from arithmetic type to arithmetic type.
  • +
  • Directly construct Target from Source on some conversions (like conversions from string to string, from char array to string, from char to char and others).
  • +

August, October 2006:

  • Better performance for many combinations of Source and Target diff --git a/lexical_cast_test.cpp b/lexical_cast_test.cpp index a994d13..3f0991b 100644 --- a/lexical_cast_test.cpp +++ b/lexical_cast_test.cpp @@ -2,7 +2,7 @@ // // See http://www.boost.org for most recent version, including documentation. // -// Copyright Terje Slettebų and Kevlin Henney, 2005. +// Copyright Terje Sletteb and Kevlin Henney, 2005. // Copyright Alexander Nasonov, 2006. // // Distributed under the Boost @@ -84,6 +84,9 @@ void test_conversion_from_to_uintmax_t(); void test_conversion_from_to_longlong(); void test_conversion_from_to_ulonglong(); #endif +void test_conversion_from_to_float(); +void test_conversion_from_to_double(); +void test_conversion_from_to_long_double(); #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION void test_traits(); void test_wtraits(); @@ -122,6 +125,9 @@ unit_test::test_suite *init_unit_test_suite(int, char *[]) suite->add(BOOST_TEST_CASE(&test_conversion_from_to_longlong)); suite->add(BOOST_TEST_CASE(&test_conversion_from_to_ulonglong)); #endif + suite->add(BOOST_TEST_CASE(&test_conversion_from_to_float)); + suite->add(BOOST_TEST_CASE(&test_conversion_from_to_double)); + suite->add(BOOST_TEST_CASE(&test_conversion_from_to_long_double)); #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION suite->add(BOOST_TEST_CASE(&test_traits)); suite->add(BOOST_TEST_CASE(&test_wtraits)); @@ -447,6 +453,24 @@ void test_conversion_from_integral_to_char(CharT zero) BOOST_CHECK_THROW(lexical_cast(t), bad_lexical_cast); } +template +void test_conversion_from_char_to_integral(CharT zero) +{ + BOOST_CHECK(lexical_cast( static_cast(zero + 0)) == static_cast(0) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 1)) == static_cast(1) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 2)) == static_cast(2) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 3)) == static_cast(3) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 4)) == static_cast(4) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 5)) == static_cast(5) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 6)) == static_cast(6) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 7)) == static_cast(7) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 8)) == static_cast(8) ); + BOOST_CHECK(lexical_cast( static_cast(zero + 9)) == static_cast(9) ); + + BOOST_CHECK_THROW(lexical_cast( static_cast(zero + 10)), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast( static_cast(zero - 1)), bad_lexical_cast); +} + template void test_conversion_from_integral_to_integral() { @@ -625,13 +649,36 @@ void test_conversion_from_to_integral() signed char const szero = '0'; unsigned char const uzero = '0'; test_conversion_from_integral_to_char(zero); + test_conversion_from_char_to_integral(zero); test_conversion_from_integral_to_char(szero); + test_conversion_from_char_to_integral(szero); test_conversion_from_integral_to_char(uzero); + test_conversion_from_char_to_integral(uzero); #if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) wchar_t const wzero = L'0'; test_conversion_from_integral_to_char(wzero); + test_conversion_from_char_to_integral(wzero); #endif + BOOST_CHECK(lexical_cast("-1") == static_cast(-1)); + BOOST_CHECK(lexical_cast("-9") == static_cast(-9)); + BOOST_CHECK(lexical_cast(-1) == static_cast(-1)); + BOOST_CHECK(lexical_cast(-9) == static_cast(-9)); + + BOOST_CHECK_THROW(lexical_cast("-1.0"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("-9.0"), bad_lexical_cast); + BOOST_CHECK(lexical_cast(-1.0) == static_cast(-1)); + BOOST_CHECK(lexical_cast(-9.0) == static_cast(-9)); + + BOOST_CHECK(lexical_cast(static_cast(1)) == static_cast(1)); + BOOST_CHECK(lexical_cast(static_cast(9)) == static_cast(9)); + BOOST_CHECK_THROW(lexical_cast(1.1f), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(1.1), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(1.1L), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(1.0001f), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(1.0001), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast(1.0001L), bad_lexical_cast); + // test_conversion_from_to_integral_for_locale typedef std::numpunct numpunct; @@ -665,6 +712,27 @@ void test_conversion_from_to_integral() BOOST_TEST_MESSAGE("Formatting with thousands_sep has not been tested"); } +template +void test_conversion_from_to_float() +{ + char const zero = '0'; + signed char const szero = '0'; + unsigned char const uzero = '0'; + test_conversion_from_integral_to_char(zero); + test_conversion_from_char_to_integral(zero); + test_conversion_from_integral_to_char(szero); + test_conversion_from_char_to_integral(szero); + test_conversion_from_integral_to_char(uzero); + test_conversion_from_char_to_integral(uzero); +#if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) + wchar_t const wzero = L'0'; + test_conversion_from_integral_to_char(wzero); + test_conversion_from_char_to_integral(wzero); +#endif + + test_conversion_from_integral_to_integral(); +} + void test_conversion_from_to_short() { test_conversion_from_to_integral(); @@ -717,6 +785,19 @@ void test_conversion_from_to_ulonglong() test_conversion_from_to_integral(); } +void test_conversion_from_to_float() +{ + test_conversion_from_to_float(); +} +void test_conversion_from_to_double() +{ + test_conversion_from_to_float(); +} +void test_conversion_from_to_long_double() +{ + test_conversion_from_to_float(); +} + #elif defined(LCAST_TEST_LONGLONG) void test_conversion_from_to_longlong()