From 1f7147d24b4081731cbd7d4dc1d0f1e4d88f722f Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 29 May 2011 19:21:28 +0000 Subject: [PATCH] mereged lexical_cast from trunk r72267 (more optimizations, updated documentation, added tests) [SVN r72268] --- include/boost/lexical_cast.hpp | 211 +++++++++++++++++++++++++-------- lexical_cast.htm | 31 ++++- lexical_cast_test.cpp | 53 +++++++++ 3 files changed, 243 insertions(+), 52 deletions(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 6875b16..c1b083b 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -247,13 +247,42 @@ namespace boost static void check_coverage() {} }; - // No specializations for: - // lcast_src_length - // lcast_src_length - // lcast_src_length - // lcast_src_length - // lcast_src_length - // lcast_src_length + template<> + struct lcast_src_length + { + BOOST_STATIC_CONSTANT(std::size_t, value = 1); + static void check_coverage() {} + }; + template<> + struct lcast_src_length + { + BOOST_STATIC_CONSTANT(std::size_t, value = 1); + static void check_coverage() {} + }; + template<> + struct lcast_src_length + { + BOOST_STATIC_CONSTANT(std::size_t, value = 1); + static void check_coverage() {} + }; + template<> + struct lcast_src_length + { + BOOST_STATIC_CONSTANT(std::size_t, value = 1); + static void check_coverage() {} + }; + template<> + struct lcast_src_length + { + BOOST_STATIC_CONSTANT(std::size_t, value = 1); + static void check_coverage() {} + }; + template<> + struct lcast_src_length + { + BOOST_STATIC_CONSTANT(std::size_t, value = 1); + static void check_coverage() {} + }; #ifndef BOOST_LCAST_NO_WCHAR_T template<> @@ -526,6 +555,10 @@ namespace boost BOOST_STATIC_ASSERT(!std::numeric_limits::is_signed); #endif + typedef typename Traits::int_type int_type; + CharT const czero = lcast_char_constants::zero; + int_type const zero = Traits::to_int_type(czero); + #ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE // TODO: use BOOST_NO_STD_LOCALE std::locale loc; @@ -533,47 +566,54 @@ namespace boost numpunct const& np = BOOST_USE_FACET(numpunct, loc); std::string const& grouping = np.grouping(); std::string::size_type const grouping_size = grouping.size(); - CharT thousands_sep = grouping_size ? np.thousands_sep() : 0; - std::string::size_type group = 0; // current group number - char last_grp_size = - grouping_size == 0 || grouping[0] <= 0 ? CHAR_MAX : grouping[0]; + + if ( grouping_size && grouping[0] > 0 ) + { + #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS // Check that ulimited group is unreachable: BOOST_STATIC_ASSERT(std::numeric_limits::digits10 < CHAR_MAX); #endif + CharT thousands_sep = np.thousands_sep(); + std::string::size_type group = 0; // current group number + char last_grp_size = grouping[0]; + char left = last_grp_size; - char left = last_grp_size; -#endif - - typedef typename Traits::int_type int_type; - CharT const czero = lcast_char_constants::zero; - int_type const zero = Traits::to_int_type(czero); - - do - { -#ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE - if(left == 0) + do { - ++group; - if(group < grouping_size) + if(left == 0) { - char const grp_size = grouping[group]; - last_grp_size = grp_size <= 0 ? CHAR_MAX : grp_size; + ++group; + if(group < grouping_size) + { + char const grp_size = grouping[group]; + last_grp_size = grp_size <= 0 ? CHAR_MAX : grp_size; + } + + left = last_grp_size; + --finish; + Traits::assign(*finish, thousands_sep); } - left = last_grp_size; + --left; + --finish; - Traits::assign(*finish, thousands_sep); - } + int_type const digit = static_cast(n % 10U); + Traits::assign(*finish, Traits::to_char_type(zero + digit)); + n /= 10; + } while(n); - --left; + } else #endif - - --finish; - int_type const digit = static_cast(n % 10U); - Traits::assign(*finish, Traits::to_char_type(zero + digit)); - n /= 10; - } while(n); + { + do + { + --finish; + int_type const digit = static_cast(n % 10U); + Traits::assign(*finish, Traits::to_char_type(zero + digit)); + n /= 10; + } while(n); + } return finish; } @@ -609,10 +649,10 @@ namespace boost /* According to [22.2.2.1.2] of Programming languages - C++ * we MUST check for correct grouping */ - if (grouping_size) + if (grouping_size && grouping[0] > 0) { unsigned char current_grouping = 0; - CharT const thousands_sep = grouping_size ? np.thousands_sep() : 0; + CharT const thousands_sep = np.thousands_sep(); char remained = grouping[current_grouping] - 1; for(;end>=begin; --end) @@ -821,9 +861,13 @@ namespace boost bool operator<<(bool); bool operator<<(char); + bool operator<<(unsigned char); + bool operator<<(signed char); #if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) bool operator<<(wchar_t); #endif + bool operator<<(unsigned char const*); + bool operator<<(signed char const*); bool operator<<(CharT const*); bool operator<<(short); bool operator<<(int); @@ -1050,6 +1094,8 @@ namespace boost } bool operator>>(CharT&); + bool operator>>(unsigned char&); + bool operator>>(signed char&); #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // This #if is in sync with lcast_streambuf_for_target @@ -1091,6 +1137,34 @@ namespace boost return true; } + template + inline bool lexical_stream_limited_src::operator<<( + unsigned char ch) + { + return ((*this) << static_cast(ch)); + } + + template + inline bool lexical_stream_limited_src::operator<<( + signed char ch) + { + return ((*this) << static_cast(ch)); + } + + template + inline bool lexical_stream_limited_src::operator<<( + unsigned char const* ch) + { + return ((*this) << reinterpret_cast(ch)); + } + + template + inline bool lexical_stream_limited_src::operator<<( + signed char const* ch) + { + return ((*this) << reinterpret_cast(ch)); + } + #if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) template inline bool lexical_stream_limited_src::operator<<( @@ -1256,6 +1330,34 @@ namespace boost return ok; } + template + inline bool lexical_stream_limited_src::operator>>( + unsigned char& output) + { + BOOST_STATIC_ASSERT( sizeof(CharT) == sizeof(unsigned char) ); + bool const ok = (finish - start == 1); + if(ok) { + CharT out; + Traits::assign(out, *start); + output = static_cast(out); + } + return ok; + } + + template + inline bool lexical_stream_limited_src::operator>>( + signed char& output) + { + BOOST_STATIC_ASSERT( sizeof(CharT) == sizeof(signed char) ); + bool const ok = (finish - start == 1); + if(ok) { + CharT out; + Traits::assign(out, *start); + output = static_cast(out); + } + return ok; + } + #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template inline bool lexical_stream_limited_src::operator>>( @@ -1312,13 +1414,9 @@ namespace boost struct lcast_streambuf_for_target { BOOST_STATIC_CONSTANT(bool, value = - ( - ::boost::type_traits::ice_or< - ::boost::type_traits::ice_not< is_integral::value >::value, - is_same::value, - is_same::value - >::value - ) + ( + ::boost::type_traits::ice_not< is_integral::value >::value + ) ); }; @@ -1472,16 +1570,29 @@ namespace boost ); }; + /* + * is_xchar_to_xchar::value is true, when + * Target and Souce are the same char types, or when + * Target and Souce are char types of the same size. + */ template struct is_xchar_to_xchar { BOOST_STATIC_CONSTANT(bool, value = - ( - ::boost::type_traits::ice_and< - is_same::value, - is_char_or_wchar::value + ( + ::boost::type_traits::ice_or< + ::boost::type_traits::ice_and< + is_same::value, + is_char_or_wchar::value + >::value, + ::boost::type_traits::ice_and< + ::boost::type_traits::ice_eq< sizeof(char),sizeof(Target)>::value, + ::boost::type_traits::ice_eq< sizeof(char),sizeof(Source)>::value, + is_char_or_wchar::value, + is_char_or_wchar::value + >::value >::value - ) + ) ); }; diff --git a/lexical_cast.htm b/lexical_cast.htm index 3372618..7929dd4 100644 --- a/lexical_cast.htm +++ b/lexical_cast.htm @@ -26,6 +26,8 @@ References
  • Changes
  • +
  • + Performance

  • Motivation

    @@ -87,7 +89,7 @@ For a good discussion of the options and issues involved in string-based formatting, including comparison of stringstream, lexical_cast, and others, see Herb Sutter's article, - The String Formatters of Manor Farm. + The String Formatters of Manor Farm. Also, take a look at the Performance section.


    Examples

    @@ -267,6 +269,8 @@ Eliminate an overhead of std::locale if your program runs in the "C

    Changes

    May 2011:

      +
    • Optimizations for "C" and other locales without number grouping.
    • +
    • Better performance and less memory usage for unsigned char and signed char conversions.
    • Better performance and less memory usage for conversions to arithmetic types.
    • Better performance and less memory usage 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).
    • @@ -312,7 +316,30 @@ Eliminate an overhead of std::locale if your program runs in the "C


    - + +

    Performance

    +This table shows the execution time in milliseconds for 100000 calls of the following string formatters: + + + + + + + + + + + + + + + + + +
    From->To lexical_cast std::stringstream
    with construction
    std::stringstream
    without construction
    sscanf/sprintf
    string->char<191710
    string->int71152318
    string->unsigned int71172217
    string->bool<11041910
    string->float851726033
    char->string71051612
    int->string151312117
    unsigned int->string141252117
    bool->string71222412
    float->string12422311548
    char*->string912320---
    int->int<112026---
    float->float<1262142---
    + +Fastest results are highlitened with green. +
    Copyright © Kevlin Henney, 2000-2005
    Copyright © Alexander Nasonov, 2006-2010
    Copyright © Antony Polukhin, 2011
    diff --git a/lexical_cast_test.cpp b/lexical_cast_test.cpp index f366eb2..f88017f 100644 --- a/lexical_cast_test.cpp +++ b/lexical_cast_test.cpp @@ -95,6 +95,7 @@ void test_wtraits(); void test_allocator(); void test_wallocator(); #endif +void test_char_types_conversions(); unit_test::test_suite *init_unit_test_suite(int, char *[]) { @@ -137,6 +138,8 @@ unit_test::test_suite *init_unit_test_suite(int, char *[]) suite->add(BOOST_TEST_CASE(&test_wallocator)); #endif + suite->add(BOOST_TEST_CASE(&test_char_types_conversions)); + return suite; } @@ -730,6 +733,12 @@ void test_conversion_from_to_integral() BOOST_CHECK(lexical_cast("+1") == static_cast(1) ); BOOST_CHECK(lexical_cast("+9") == static_cast(9) ); + BOOST_CHECK(lexical_cast("+10") == static_cast(10) ); + BOOST_CHECK(lexical_cast("+90") == static_cast(90) ); + BOOST_CHECK_THROW(lexical_cast("++1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("-+9"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("--1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("+-9"), bad_lexical_cast); // test_conversion_from_to_integral_for_locale typedef std::numpunct numpunct; @@ -785,6 +794,11 @@ void test_conversion_from_to_float() BOOST_CHECK_CLOSE(lexical_cast("+1"), 1, std::numeric_limits::epsilon() ); BOOST_CHECK_CLOSE(lexical_cast("+9"), 9, std::numeric_limits::epsilon()*9 ); + + BOOST_CHECK_THROW(lexical_cast("++1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("-+9"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("--1"), bad_lexical_cast); + BOOST_CHECK_THROW(lexical_cast("+-9"), bad_lexical_cast); } void test_conversion_from_to_short() @@ -923,3 +937,42 @@ void test_wallocator() #endif +void test_char_types_conversions() +{ + const char c_arr[] = "Test array of chars"; + const unsigned char uc_arr[] = "Test array of chars"; + const signed char sc_arr[] = "Test array of chars"; + + BOOST_CHECK(boost::lexical_cast(c_arr) == std::string(c_arr)); + BOOST_CHECK(boost::lexical_cast(uc_arr) == std::string(c_arr)); + BOOST_CHECK(boost::lexical_cast(sc_arr) == std::string(c_arr)); + + BOOST_CHECK(boost::lexical_cast(c_arr[0]) == c_arr[0]); + BOOST_CHECK(boost::lexical_cast(uc_arr[0]) == c_arr[0]); + BOOST_CHECK(boost::lexical_cast(sc_arr[0]) == c_arr[0]); + + BOOST_CHECK(boost::lexical_cast(c_arr[0]) == uc_arr[0]); + BOOST_CHECK(boost::lexical_cast(uc_arr[0]) == uc_arr[0]); + BOOST_CHECK(boost::lexical_cast(sc_arr[0]) == uc_arr[0]); + + BOOST_CHECK(boost::lexical_cast(c_arr[0]) == sc_arr[0]); + BOOST_CHECK(boost::lexical_cast(uc_arr[0]) == sc_arr[0]); + BOOST_CHECK(boost::lexical_cast(sc_arr[0]) == sc_arr[0]); + +#ifndef BOOST_LCAST_NO_WCHAR_T + const wchar_t wc_arr[]=L"Test array of chars"; + + BOOST_CHECK(boost::lexical_cast(wc_arr) == std::wstring(wc_arr)); + BOOST_CHECK(boost::lexical_cast(c_arr) == std::wstring(wc_arr)); + + BOOST_CHECK(boost::lexical_cast(sc_arr) != std::wstring(wc_arr) ); + BOOST_CHECK(boost::lexical_cast(uc_arr) != std::wstring(wc_arr) ); + + BOOST_CHECK(boost::lexical_cast(c_arr[0]) == wc_arr[0]); + BOOST_CHECK(boost::lexical_cast(wc_arr[0]) == wc_arr[0]); + + BOOST_CHECK_THROW(boost::lexical_cast(uc_arr[0]), bad_lexical_cast); + BOOST_CHECK_THROW(boost::lexical_cast(sc_arr[0]), bad_lexical_cast); + +#endif +}