From b40bc02f94bf9cf693226d56d66306ebc68326ce Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 13 Dec 2013 13:52:18 +0400 Subject: [PATCH] try_lexical_cast is now implemented in optimal way --- include/boost/lexical_cast.hpp | 236 ++++++++++++------------- test/lexical_cast_float_types_test.cpp | 8 +- 2 files changed, 118 insertions(+), 126 deletions(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 1ae950d..280de8a 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -2058,9 +2058,9 @@ namespace boost { {}; template - struct lexical_cast_do_cast + struct lexical_converter_impl { - static inline bool try_lexical_cast_impl(const Source& arg, Target& result) + static inline bool try_convert(const Source& arg, Target& result) { typedef lexical_cast_stream_traits stream_trait; @@ -2090,90 +2090,94 @@ namespace boost { return true; } - - static inline Target lexical_cast_impl(const Source& arg) - { - // Target type must be default constructible - Target result; - - if(!try_lexical_cast_impl(arg, result)) - BOOST_LCAST_THROW_BAD_CAST(Source, Target); - - return result; - } }; template - struct lexical_cast_copy + struct copy_converter_impl { - static inline bool try_lexical_cast_impl(const Source& arg, Target& result) + static inline bool try_convert(const Source& arg, Target& result) { result = arg; return true; } + }; - static inline const Source& lexical_cast_impl(const Source &arg) BOOST_NOEXCEPT - { - return arg; + template + struct detect_precision_loss + { + typedef Source source_type; + typedef boost::numeric::Trunc Rounder; + typedef BOOST_DEDUCED_TYPENAME mpl::if_< + boost::is_arithmetic, Source, Source const& + >::type argument_type ; + + static inline source_type nearbyint(argument_type s, bool& is_ok) BOOST_NOEXCEPT { + const source_type near_int = Rounder::nearbyint(s); + if (near_int && is_ok) { + const source_type orig_div_round = s / near_int; + const source_type eps = std::numeric_limits::epsilon(); + + is_ok = !((orig_div_round > 1 ? orig_div_round - 1 : 1 - orig_div_round) > eps); + } + + return s; + } + + typedef typename Rounder::round_style round_style; + }; + + template + struct fake_precision_loss: public Base + { + typedef Source source_type ; + typedef BOOST_DEDUCED_TYPENAME mpl::if_< + boost::is_arithmetic, Source, Source const& + >::type argument_type ; + + static inline source_type nearbyint(argument_type s, bool& /*is_ok*/) BOOST_NOEXCEPT { + return s; } }; - template - struct detect_precision_loss - { - typedef boost::numeric::Trunc Rounder; - typedef Source source_type ; - - typedef BOOST_DEDUCED_TYPENAME mpl::if_< - boost::is_arithmetic, Source, Source const& - >::type argument_type ; - - static source_type nearbyint ( argument_type s ) - { - const source_type near_int = Rounder::nearbyint(s); - if (near_int) { - const source_type orig_div_round = s / near_int; - const source_type eps = std::numeric_limits::epsilon(); - - if ((orig_div_round > 1 ? orig_div_round - 1 : 1 - orig_div_round) > eps) - BOOST_LCAST_THROW_BAD_CAST(Source, Target); - } - - return s ; - } - - typedef typename Rounder::round_style round_style; - } ; - - template struct nothrow_overflow_handler { - void operator() ( boost::numeric::range_check_result r ) - { - if (r != boost::numeric::cInRange) - BOOST_LCAST_THROW_BAD_CAST(Source, Target); - } - } ; + inline bool operator() ( boost::numeric::range_check_result r ) const BOOST_NOEXCEPT { + return (r == boost::numeric::cInRange); + } + }; + + template + inline bool stateful_numeric_convert(const Source& arg, Target& result) BOOST_NOEXCEPT { + typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< + boost::is_base_of< detect_precision_loss, Converter >::value, + Converter, + fake_precision_loss + >::type converter_t; + + bool res = nothrow_overflow_handler()(converter_t::out_of_range(arg)); + result = converter_t::low_level_convert(converter_t::nearbyint(arg, res)); + return res; + } template struct lexical_cast_dynamic_num_not_ignoring_minus { - static inline Target lexical_cast_impl(const Source &arg) + static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT { - return boost::numeric::converter< + return stateful_numeric_convert, - nothrow_overflow_handler, - detect_precision_loss - >::convert(arg); + nothrow_overflow_handler, + detect_precision_loss + > >(arg, result); } }; template struct lexical_cast_dynamic_num_ignoring_minus { - static inline Target lexical_cast_impl(const Source &arg) + static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT { typedef BOOST_DEDUCED_TYPENAME boost::mpl::eval_if_c< boost::is_float::value, @@ -2184,14 +2188,18 @@ namespace boost { typedef boost::numeric::converter< Target, usource_t, - boost::numeric::conversion_traits, - nothrow_overflow_handler, - detect_precision_loss + boost::numeric::conversion_traits, + nothrow_overflow_handler, + detect_precision_loss > converter_t; - - return ( - arg < 0 ? static_cast(0u - converter_t::convert(0u - arg)) : converter_t::convert(arg) - ); + + if (arg < 0) { + const bool res = stateful_numeric_convert(0u - arg, result); + result = static_cast(0u - result); + return res; + } else { + return stateful_numeric_convert(arg, result); + } } }; @@ -2214,20 +2222,9 @@ namespace boost { * and the result will be the two's complement. */ template - struct lexical_cast_dynamic_num + struct dynamic_num_converter_impl { - static inline bool try_lexical_cast_impl(const Source& arg, Target& result) - { - try { - result = lexical_cast_impl(arg); - } catch (const bad_lexical_cast& e) { - return false; - } - - return true; - } - - static inline Target lexical_cast_impl(const Source &arg) + static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT { typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< boost::type_traits::ice_and< @@ -2247,64 +2244,59 @@ namespace boost { lexical_cast_dynamic_num_not_ignoring_minus >::type caster_type; - return caster_type::lexical_cast_impl(arg); + return caster_type::try_convert(arg, result); } }; + } - template - struct lexical_cast_caster_type_detector { - typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay::type src; - typedef BOOST_DEDUCED_TYPENAME boost::type_traits::ice_or< - boost::detail::is_xchar_to_xchar::value, - boost::detail::is_char_array_to_stdstring::value, - boost::type_traits::ice_and< - boost::is_same::value, - boost::detail::is_stdstring::value - >::value, - boost::type_traits::ice_and< - boost::is_same::value, - boost::detail::is_character::value - >::value - > shall_we_copy_t; + template + inline bool try_lexical_cast(const Source &arg, Target& result) + { + typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay::type src; - typedef boost::detail::is_arithmetic_and_not_xchars - shall_we_copy_with_dynamic_check_t; + typedef BOOST_DEDUCED_TYPENAME boost::type_traits::ice_or< + boost::detail::is_xchar_to_xchar::value, + boost::detail::is_char_array_to_stdstring::value, + boost::type_traits::ice_and< + boost::is_same::value, + boost::detail::is_stdstring::value + >::value, + boost::type_traits::ice_and< + boost::is_same::value, + boost::detail::is_character::value + >::value + > shall_we_copy_t; - // We do evaluate second `if_` lazily to avoid unnecessary instantiations - // of `shall_we_copy_with_dynamic_check_t` and improve compilation times. - typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< - shall_we_copy_t::value, - boost::mpl::identity >, - boost::mpl::if_< - shall_we_copy_with_dynamic_check_t, - boost::detail::lexical_cast_dynamic_num, - boost::detail::lexical_cast_do_cast - > - >::type caster_type_lazy; + typedef boost::detail::is_arithmetic_and_not_xchars + shall_we_copy_with_dynamic_check_t; - typedef BOOST_DEDUCED_TYPENAME caster_type_lazy::type caster_type; - }; + // We do evaluate second `if_` lazily to avoid unnecessary instantiations + // of `shall_we_copy_with_dynamic_check_t` and improve compilation times. + typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< + shall_we_copy_t::value, + boost::mpl::identity >, + boost::mpl::if_< + shall_we_copy_with_dynamic_check_t, + boost::detail::dynamic_num_converter_impl, + boost::detail::lexical_converter_impl + > + >::type caster_type_lazy; + + typedef BOOST_DEDUCED_TYPENAME caster_type_lazy::type caster_type; + + return caster_type::try_convert(arg, result); } template inline Target lexical_cast(const Source &arg) { - typedef BOOST_DEDUCED_TYPENAME - boost::detail::lexical_cast_caster_type_detector - ::caster_type caster_type; + Target result; - return caster_type::lexical_cast_impl(arg); - } + if (!try_lexical_cast(arg, result)) + BOOST_LCAST_THROW_BAD_CAST(Source, Target); - template - inline bool try_lexical_cast(const Source &arg, Target& result) - { - typedef BOOST_DEDUCED_TYPENAME - boost::detail::lexical_cast_caster_type_detector - ::caster_type caster_type; - - return caster_type::try_lexical_cast_impl(arg, result); + return result; } template diff --git a/test/lexical_cast_float_types_test.cpp b/test/lexical_cast_float_types_test.cpp index 0179865..557d146 100644 --- a/test/lexical_cast_float_types_test.cpp +++ b/test/lexical_cast_float_types_test.cpp @@ -111,8 +111,8 @@ void test_conversion_from_to_float_for_locale() #ifndef BOOST_LCAST_NO_WCHAR_T #define CHECK_CLOSE_ABS_DIFF(VAL,PREFIX) \ converted_val = lexical_cast(#VAL); \ - BOOST_CHECK_CLOSE_FRACTION( (VAL ## L? VAL ## L : std::numeric_limits::epsilon()), \ - (converted_val ? converted_val : std::numeric_limits::epsilon()), \ + BOOST_CHECK_CLOSE_FRACTION( (static_cast(VAL ## L)? VAL ## L : std::numeric_limits::epsilon()), \ + (converted_val ? converted_val : std::numeric_limits::epsilon()),\ std::numeric_limits::epsilon() \ ); \ BOOST_CHECK_EQUAL(converted_val, lexical_cast(L## #VAL) ); @@ -120,8 +120,8 @@ void test_conversion_from_to_float_for_locale() #else #define CHECK_CLOSE_ABS_DIFF(VAL,TYPE) \ converted_val = lexical_cast(#VAL); \ - BOOST_CHECK_CLOSE_FRACTION( (VAL ## L? VAL ## L : std::numeric_limits::epsilon()), \ - (converted_val ? converted_val : std::numeric_limits::epsilon()), \ + BOOST_CHECK_CLOSE_FRACTION( (static_cast(VAL ## L)? VAL ## L : std::numeric_limits::epsilon()), \ + (converted_val ? converted_val : std::numeric_limits::epsilon()),\ std::numeric_limits::epsilon() \ ); #endif