From 07082420b3e70412666800f5c2e7b243477ea3e0 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Thu, 12 Dec 2013 18:41:51 +0400 Subject: [PATCH 01/10] Sketch implementaion of try_lexical_cast --- include/boost/lexical_cast.hpp | 168 +++++++++++++++++++++++++-------- 1 file changed, 129 insertions(+), 39 deletions(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 5b43501..1ae950d 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -2060,7 +2060,7 @@ namespace boost { template struct lexical_cast_do_cast { - static inline Target lexical_cast_impl(const Source& arg) + static inline bool try_lexical_cast_impl(const Source& arg, Target& result) { typedef lexical_cast_stream_traits stream_trait; @@ -2076,27 +2076,42 @@ namespace boost { BOOST_DEDUCED_TYPENAME stream_trait::traits > o_interpreter_type; - // Target type must be default constructible - Target result; - i_interpreter_type i_interpreter; // Disabling ADL, by directly specifying operators. - const bool input_ok = (i_interpreter.operator <<(arg)); + if (!(i_interpreter.operator <<(arg))) + return false; o_interpreter_type out(i_interpreter.cbegin(), i_interpreter.cend()); // Disabling ADL, by directly specifying operators. - if(!input_ok || !(out.operator >>(result))) - BOOST_LCAST_THROW_BAD_CAST(Source, Target); + if(!(out.operator >>(result))) + return false; + + 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 + template struct lexical_cast_copy { + static inline bool try_lexical_cast_impl(const Source& arg, Target& result) + { + result = arg; + return true; + } + static inline const Source& lexical_cast_impl(const Source &arg) BOOST_NOEXCEPT { return arg; @@ -2201,6 +2216,17 @@ namespace boost { template struct lexical_cast_dynamic_num { + 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) { typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< @@ -2224,46 +2250,63 @@ namespace boost { return caster_type::lexical_cast_impl(arg); } }; + + 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; + + typedef boost::detail::is_arithmetic_and_not_xchars + shall_we_copy_with_dynamic_check_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_DEDUCED_TYPENAME caster_type_lazy::type caster_type; + }; } template inline Target lexical_cast(const Source &arg) { - 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; - - typedef boost::detail::is_arithmetic_and_not_xchars - shall_we_copy_with_dynamic_check_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_DEDUCED_TYPENAME caster_type_lazy::type caster_type; + typedef BOOST_DEDUCED_TYPENAME + boost::detail::lexical_cast_caster_type_detector + ::caster_type caster_type; return caster_type::lexical_cast_impl(arg); } + 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); + } + template inline Target lexical_cast(const char* chars, std::size_t count) { @@ -2272,6 +2315,13 @@ namespace boost { ); } + template + inline bool try_lexical_cast(const char* chars, std::size_t count, Target& result) + { + return ::boost::try_lexical_cast( + ::boost::iterator_range(chars, chars + count), result + ); + } template inline Target lexical_cast(const unsigned char* chars, std::size_t count) @@ -2281,6 +2331,14 @@ namespace boost { ); } + template + inline bool try_lexical_cast(const unsigned char* chars, std::size_t count, Target& result) + { + return ::boost::try_lexical_cast( + ::boost::iterator_range(chars, chars + count), result + ); + } + template inline Target lexical_cast(const signed char* chars, std::size_t count) { @@ -2289,6 +2347,14 @@ namespace boost { ); } + template + inline bool try_lexical_cast(const signed char* chars, std::size_t count, Target& result) + { + return ::boost::try_lexical_cast( + ::boost::iterator_range(chars, chars + count), result + ); + } + #ifndef BOOST_LCAST_NO_WCHAR_T template inline Target lexical_cast(const wchar_t* chars, std::size_t count) @@ -2297,6 +2363,14 @@ namespace boost { ::boost::iterator_range(chars, chars + count) ); } + + template + inline bool try_lexical_cast(const wchar_t* chars, std::size_t count, Target& result) + { + return ::boost::try_lexical_cast( + ::boost::iterator_range(chars, chars + count), result + ); + } #endif #ifndef BOOST_NO_CXX11_CHAR16_T template @@ -2306,6 +2380,14 @@ namespace boost { ::boost::iterator_range(chars, chars + count) ); } + + template + inline bool try_lexical_cast(const char16_t* chars, std::size_t count, Target& result) + { + return ::boost::try_lexical_cast( + ::boost::iterator_range(chars, chars + count), result + ); + } #endif #ifndef BOOST_NO_CXX11_CHAR32_T template @@ -2315,6 +2397,14 @@ namespace boost { ::boost::iterator_range(chars, chars + count) ); } + + template + inline bool try_lexical_cast(const char32_t* chars, std::size_t count, Target& result) + { + return ::boost::try_lexical_cast( + ::boost::iterator_range(chars, chars + count), result + ); + } #endif } // namespace boost From b40bc02f94bf9cf693226d56d66306ebc68326ce Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 13 Dec 2013 13:52:18 +0400 Subject: [PATCH 02/10] 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 From e3dec04dd51d40e564b37c96bc622906e6392ff8 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 16 Dec 2013 18:47:41 +0400 Subject: [PATCH 03/10] Applied improvements from `[boost] [conversion] try_lexical_cast` discussion: function is called try_lexical_convert and now uses perfect forwardings when possible. --- include/boost/lexical_cast.hpp | 142 +++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 61 deletions(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 280de8a..3986f00 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -2060,26 +2060,35 @@ namespace boost { template struct lexical_converter_impl { - static inline bool try_convert(const Source& arg, Target& result) - { - typedef lexical_cast_stream_traits stream_trait; - - typedef detail::lexical_istream_limited_src< - BOOST_DEDUCED_TYPENAME stream_trait::char_type, - BOOST_DEDUCED_TYPENAME stream_trait::traits, - stream_trait::requires_stringbuf, - stream_trait::len_t::value + 1 - > i_interpreter_type; + typedef lexical_cast_stream_traits stream_trait; - typedef detail::lexical_ostream_limited_src< - BOOST_DEDUCED_TYPENAME stream_trait::char_type, - BOOST_DEDUCED_TYPENAME stream_trait::traits - > o_interpreter_type; + typedef detail::lexical_istream_limited_src< + BOOST_DEDUCED_TYPENAME stream_trait::char_type, + BOOST_DEDUCED_TYPENAME stream_trait::traits, + stream_trait::requires_stringbuf, + stream_trait::len_t::value + 1 + > i_interpreter_type; + typedef detail::lexical_ostream_limited_src< + BOOST_DEDUCED_TYPENAME stream_trait::char_type, + BOOST_DEDUCED_TYPENAME stream_trait::traits + > o_interpreter_type; + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template + static inline bool try_convert(T&& arg, Target& result) { + typedef T&& forward_type; +#else + static inline bool try_convert(const Source& arg, Target& result) { + typedef const Source& forward_type; +#endif i_interpreter_type i_interpreter; // Disabling ADL, by directly specifying operators. - if (!(i_interpreter.operator <<(arg))) + // + // For compilers with perfect forwarding `static_cast(arg)` is + // eqaul to `std::forward(arg)`. + if (!(i_interpreter.operator <<(static_cast(arg)))) return false; o_interpreter_type out(i_interpreter.cbegin(), i_interpreter.cend()); @@ -2095,11 +2104,18 @@ namespace boost { template struct copy_converter_impl { - static inline bool try_convert(const Source& arg, Target& result) - { +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template + static inline bool try_convert(T&& arg, Target& result) { + result = static_cast(arg); // eqaul to `result = std::forward(arg);` + return true; + } +#else + static inline bool try_convert(const Source& arg, Target& result) { result = arg; return true; } +#endif }; template @@ -2146,12 +2162,20 @@ namespace boost { } }; - template - inline bool stateful_numeric_convert(const Source& arg, Target& result) BOOST_NOEXCEPT { + template + inline bool noexcept_numeric_convert(const Source& arg, Target& result) BOOST_NOEXCEPT { + typedef boost::numeric::converter< + Target, + Source, + boost::numeric::conversion_traits, + nothrow_overflow_handler, + detect_precision_loss + > converter_orig_t; + typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< - boost::is_base_of< detect_precision_loss, Converter >::value, - Converter, - fake_precision_loss + boost::is_base_of< detect_precision_loss, converter_orig_t >::value, + converter_orig_t, + fake_precision_loss >::type converter_t; bool res = nothrow_overflow_handler()(converter_t::out_of_range(arg)); @@ -2162,43 +2186,27 @@ namespace boost { template struct lexical_cast_dynamic_num_not_ignoring_minus { - static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT - { - return stateful_numeric_convert, - nothrow_overflow_handler, - detect_precision_loss - > >(arg, result); + static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT { + return noexcept_numeric_convert(arg, result); } }; template struct lexical_cast_dynamic_num_ignoring_minus { - static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT - { + 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, boost::mpl::identity, boost::make_unsigned >::type usource_t; - - typedef boost::numeric::converter< - Target, - usource_t, - boost::numeric::conversion_traits, - nothrow_overflow_handler, - detect_precision_loss - > converter_t; if (arg < 0) { - const bool res = stateful_numeric_convert(0u - arg, result); + const bool res = noexcept_numeric_convert(0u - arg, result); result = static_cast(0u - result); return res; } else { - return stateful_numeric_convert(arg, result); + return noexcept_numeric_convert(arg, result); } } }; @@ -2224,8 +2232,7 @@ namespace boost { template struct dynamic_num_converter_impl { - static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT - { + 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< boost::is_unsigned::value, @@ -2250,10 +2257,23 @@ namespace boost { } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template - inline bool try_lexical_cast(const Source &arg, Target& result) + inline bool try_lexical_convert(Source&& arg, Target& result) { - typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay::type src; + typedef BOOST_DEDUCED_TYPENAME boost::remove_const< + BOOST_DEDUCED_TYPENAME boost::remove_reference::type + >::type no_cr_source_t; + + typedef Source&& forward_type; +#else + template + inline bool try_lexical_convert(const Source& arg, Target& result) + { + typedef Source no_cr_source_t; + typedef const Source& forward_type; +#endif + 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, @@ -2285,7 +2305,7 @@ namespace boost { typedef BOOST_DEDUCED_TYPENAME caster_type_lazy::type caster_type; - return caster_type::try_convert(arg, result); + return caster_type::try_convert(static_cast(arg), result); } template @@ -2293,7 +2313,7 @@ namespace boost { { Target result; - if (!try_lexical_cast(arg, result)) + if (!try_lexical_convert(arg, result)) BOOST_LCAST_THROW_BAD_CAST(Source, Target); return result; @@ -2308,9 +2328,9 @@ namespace boost { } template - inline bool try_lexical_cast(const char* chars, std::size_t count, Target& result) + inline bool try_lexical_convert(const char* chars, std::size_t count, Target& result) { - return ::boost::try_lexical_cast( + return ::boost::try_lexical_convert( ::boost::iterator_range(chars, chars + count), result ); } @@ -2324,9 +2344,9 @@ namespace boost { } template - inline bool try_lexical_cast(const unsigned char* chars, std::size_t count, Target& result) + inline bool try_lexical_convert(const unsigned char* chars, std::size_t count, Target& result) { - return ::boost::try_lexical_cast( + return ::boost::try_lexical_convert( ::boost::iterator_range(chars, chars + count), result ); } @@ -2340,9 +2360,9 @@ namespace boost { } template - inline bool try_lexical_cast(const signed char* chars, std::size_t count, Target& result) + inline bool try_lexical_convert(const signed char* chars, std::size_t count, Target& result) { - return ::boost::try_lexical_cast( + return ::boost::try_lexical_convert( ::boost::iterator_range(chars, chars + count), result ); } @@ -2357,9 +2377,9 @@ namespace boost { } template - inline bool try_lexical_cast(const wchar_t* chars, std::size_t count, Target& result) + inline bool try_lexical_convert(const wchar_t* chars, std::size_t count, Target& result) { - return ::boost::try_lexical_cast( + return ::boost::try_lexical_convert( ::boost::iterator_range(chars, chars + count), result ); } @@ -2374,9 +2394,9 @@ namespace boost { } template - inline bool try_lexical_cast(const char16_t* chars, std::size_t count, Target& result) + inline bool try_lexical_convert(const char16_t* chars, std::size_t count, Target& result) { - return ::boost::try_lexical_cast( + return ::boost::try_lexical_convert( ::boost::iterator_range(chars, chars + count), result ); } @@ -2391,9 +2411,9 @@ namespace boost { } template - inline bool try_lexical_cast(const char32_t* chars, std::size_t count, Target& result) + inline bool try_lexical_convert(const char32_t* chars, std::size_t count, Target& result) { - return ::boost::try_lexical_cast( + return ::boost::try_lexical_convert( ::boost::iterator_range(chars, chars + count), result ); } From 91b77e7373d456d732f7d2efbc2cb6da5b6ab7bb Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 16 Dec 2013 19:05:43 +0400 Subject: [PATCH 04/10] Do forward input streamable parameter during lexical input streaming. --- include/boost/lexical_cast.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 3986f00..dfd833d 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -1394,7 +1394,14 @@ namespace boost { } template +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + bool shl_input_streamable(InputStreamable&& input) { + typedef InputStreamable&& forward_type; +#else bool shl_input_streamable(InputStreamable& input) { + typedef InputStreamable& forward_type; +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES + #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_LOCALE) // If you have compilation error at this point, than your STL library // does not support such conversions. Try updating it. @@ -1405,7 +1412,7 @@ namespace boost { out_stream.exceptions(std::ios::badbit); try { #endif - bool const result = !(out_stream << input).fail(); + bool const result = !(out_stream << static_cast(input)).fail(); const buffer_t* const p = static_cast( static_cast*>(out_stream.rdbuf()) ); From 1707131ac3588c54303c0ddb839d9e05b0463c32 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Wed, 1 Jan 2014 21:47:09 +0400 Subject: [PATCH 05/10] Added some tests for try_lexical_convert and fixed some issues. --- include/boost/lexical_cast.hpp | 171 ++++++++-------------- test/Jamfile.v2 | 1 + test/lexical_cast_try_lexical_convert.cpp | 78 ++++++++++ 3 files changed, 143 insertions(+), 107 deletions(-) create mode 100644 test/lexical_cast_try_lexical_convert.cpp diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index dfd833d..cf1d700 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -1394,14 +1394,7 @@ namespace boost { } template -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES - bool shl_input_streamable(InputStreamable&& input) { - typedef InputStreamable&& forward_type; -#else bool shl_input_streamable(InputStreamable& input) { - typedef InputStreamable& forward_type; -#endif // BOOST_NO_CXX11_RVALUE_REFERENCES - #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_LOCALE) // If you have compilation error at this point, than your STL library // does not support such conversions. Try updating it. @@ -1412,7 +1405,7 @@ namespace boost { out_stream.exceptions(std::ios::badbit); try { #endif - bool const result = !(out_stream << static_cast(input)).fail(); + bool const result = !(out_stream << input).fail(); const buffer_t* const p = static_cast( static_cast*>(out_stream.rdbuf()) ); @@ -1673,7 +1666,6 @@ namespace boost { return ((*this) << reinterpret_cast const& >(input)); } #endif - template bool operator<<(const InStreamable& input) { return shl_input_streamable(input); } }; @@ -2081,21 +2073,14 @@ namespace boost { BOOST_DEDUCED_TYPENAME stream_trait::traits > o_interpreter_type; -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES - template - static inline bool try_convert(T&& arg, Target& result) { - typedef T&& forward_type; -#else static inline bool try_convert(const Source& arg, Target& result) { - typedef const Source& forward_type; -#endif i_interpreter_type i_interpreter; // Disabling ADL, by directly specifying operators. // // For compilers with perfect forwarding `static_cast(arg)` is // eqaul to `std::forward(arg)`. - if (!(i_interpreter.operator <<(static_cast(arg)))) + if (!(i_interpreter.operator <<(arg))) return false; o_interpreter_type out(i_interpreter.cbegin(), i_interpreter.cend()); @@ -2263,56 +2248,76 @@ namespace boost { }; } + namespace conversion { namespace detail { #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES - template - inline bool try_lexical_convert(Source&& arg, Target& result) - { - typedef BOOST_DEDUCED_TYPENAME boost::remove_const< - BOOST_DEDUCED_TYPENAME boost::remove_reference::type - >::type no_cr_source_t; + template + inline bool try_lexical_convert(Source&& arg, Target& result) + { + typedef BOOST_DEDUCED_TYPENAME boost::remove_const< + BOOST_DEDUCED_TYPENAME boost::remove_reference::type + >::type no_cr_source_t; - typedef Source&& forward_type; + typedef Source&& forward_type; #else - template - inline bool try_lexical_convert(const Source& arg, Target& result) - { - typedef Source no_cr_source_t; - typedef const Source& forward_type; + template + inline bool try_lexical_convert(const Source& arg, Target& result) + { + typedef Source no_cr_source_t; + typedef const Source& forward_type; #endif - typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay::type src; + 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; + 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; - typedef boost::detail::is_arithmetic_and_not_xchars - shall_we_copy_with_dynamic_check_t; + typedef boost::detail::is_arithmetic_and_not_xchars + shall_we_copy_with_dynamic_check_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::dynamic_num_converter_impl, - boost::detail::lexical_converter_impl - > - >::type caster_type_lazy; + // 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; + typedef BOOST_DEDUCED_TYPENAME caster_type_lazy::type caster_type; - return caster_type::try_convert(static_cast(arg), result); + return caster_type::try_convert(static_cast(arg), result); + } + + template + inline bool try_lexical_convert(const CharacterT* chars, std::size_t count, Target& result) + { + BOOST_STATIC_ASSERT_MSG( + boost::detail::is_character::value, + "This overload of try_lexical_convert is meant to be used only with arrays of characters." + ); + return ::boost::conversion::detail::try_lexical_convert( + ::boost::iterator_range(chars, chars + count), result + ); + } + + }} // namespace conversion::detail + + namespace conversion { + // ADL barrier + using ::boost::conversion::detail::try_lexical_convert; } template @@ -2320,7 +2325,7 @@ namespace boost { { Target result; - if (!try_lexical_convert(arg, result)) + if (!boost::conversion::detail::try_lexical_convert(arg, result)) BOOST_LCAST_THROW_BAD_CAST(Source, Target); return result; @@ -2334,27 +2339,11 @@ namespace boost { ); } - template - inline bool try_lexical_convert(const char* chars, std::size_t count, Target& result) - { - return ::boost::try_lexical_convert( - ::boost::iterator_range(chars, chars + count), result - ); - } - template inline Target lexical_cast(const unsigned char* chars, std::size_t count) { - return ::boost::lexical_cast( + return ::boost::lexical_cast( ::boost::iterator_range(chars, chars + count) - ); - } - - template - inline bool try_lexical_convert(const unsigned char* chars, std::size_t count, Target& result) - { - return ::boost::try_lexical_convert( - ::boost::iterator_range(chars, chars + count), result ); } @@ -2366,14 +2355,6 @@ namespace boost { ); } - template - inline bool try_lexical_convert(const signed char* chars, std::size_t count, Target& result) - { - return ::boost::try_lexical_convert( - ::boost::iterator_range(chars, chars + count), result - ); - } - #ifndef BOOST_LCAST_NO_WCHAR_T template inline Target lexical_cast(const wchar_t* chars, std::size_t count) @@ -2382,14 +2363,6 @@ namespace boost { ::boost::iterator_range(chars, chars + count) ); } - - template - inline bool try_lexical_convert(const wchar_t* chars, std::size_t count, Target& result) - { - return ::boost::try_lexical_convert( - ::boost::iterator_range(chars, chars + count), result - ); - } #endif #ifndef BOOST_NO_CXX11_CHAR16_T template @@ -2399,14 +2372,6 @@ namespace boost { ::boost::iterator_range(chars, chars + count) ); } - - template - inline bool try_lexical_convert(const char16_t* chars, std::size_t count, Target& result) - { - return ::boost::try_lexical_convert( - ::boost::iterator_range(chars, chars + count), result - ); - } #endif #ifndef BOOST_NO_CXX11_CHAR32_T template @@ -2416,14 +2381,6 @@ namespace boost { ::boost::iterator_range(chars, chars + count) ); } - - template - inline bool try_lexical_convert(const char32_t* chars, std::size_t count, Target& result) - { - return ::boost::try_lexical_convert( - ::boost::iterator_range(chars, chars + count), result - ); - } #endif } // namespace boost diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 1ac10ae..3acf6b4 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -62,6 +62,7 @@ test-suite conversion [ run lexical_cast_stream_traits_test.cpp ] [ compile-fail lexical_cast_to_pointer_test.cpp ] [ run lexical_cast_filesystem_test.cpp ../../filesystem/build//boost_filesystem/static ] + [ run lexical_cast_try_lexical_convert.cpp ] ; # Assuring that examples compile and run. Adding sources from `example` directory to the `conversion` test suite. diff --git a/test/lexical_cast_try_lexical_convert.cpp b/test/lexical_cast_try_lexical_convert.cpp new file mode 100644 index 0000000..0c7c4de --- /dev/null +++ b/test/lexical_cast_try_lexical_convert.cpp @@ -0,0 +1,78 @@ +// Unit test for boost::lexical_cast. +// +// See http://www.boost.org for most recent version, including documentation. +// +// Copyright Antony Polukhin, 2014. +// +// 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). + +#include + +#include +#include + +using namespace boost; + +void try_uncommon_cases() +{ + std::string sres; + const bool res1 = conversion::try_lexical_convert(std::string("Test string"), sres); + BOOST_CHECK(res1); + BOOST_CHECK_EQUAL(sres, "Test string"); + + volatile int vires; + const bool res2 = conversion::try_lexical_convert(100, vires); + BOOST_CHECK(res2); + BOOST_CHECK_EQUAL(vires, 100); + + const bool res3 = conversion::try_lexical_convert("Test string", sres); + BOOST_CHECK(res3); + BOOST_CHECK_EQUAL(sres, "Test string"); + + const bool res4 = conversion::try_lexical_convert("Test string", sizeof("Test string") - 1, sres); + BOOST_CHECK(res4); + BOOST_CHECK_EQUAL(sres, "Test string"); + + int ires; + BOOST_CHECK(!conversion::try_lexical_convert("Test string", ires)); + BOOST_CHECK(!conversion::try_lexical_convert(1.1, ires)); + BOOST_CHECK(!conversion::try_lexical_convert(-1.9, ires)); + BOOST_CHECK(!conversion::try_lexical_convert("1.1", ires)); + BOOST_CHECK(!conversion::try_lexical_convert("1000000000000000000000000000000000000000", ires)); +} + + +void try_common_cases() +{ + int ires = 0; + const bool res1 = conversion::try_lexical_convert(std::string("100"), ires); + BOOST_CHECK(res1); + BOOST_CHECK_EQUAL(ires, 100); + + ires = 0; + const bool res2 = conversion::try_lexical_convert("-100", ires); + BOOST_CHECK(res2); + BOOST_CHECK_EQUAL(ires, -100); + + float fres = 1.0f; + const bool res3 = conversion::try_lexical_convert("0.0", fres); + BOOST_CHECK(res3); + BOOST_CHECK_EQUAL(fres, 0.0f); + + fres = 1.0f; + const bool res4 = conversion::try_lexical_convert("0.0", sizeof("0.0") - 1, fres); + BOOST_CHECK(res4); + BOOST_CHECK_EQUAL(fres, 0.0f); +} + +unit_test::test_suite *init_unit_test_suite(int, char *[]) +{ + unit_test::test_suite *suite = + BOOST_TEST_SUITE("Tests for try_lexical_convert"); + suite->add(BOOST_TEST_CASE(&try_uncommon_cases)); + suite->add(BOOST_TEST_CASE(&try_common_cases)); + + return suite; +} From e8954f3868abe71e769c1de7d7519a8bb53bc460 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sat, 4 Jan 2014 20:26:19 +0400 Subject: [PATCH 06/10] Added docs for try_lexical_convert --- doc/lexical_cast.qbk | 48 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/doc/lexical_cast.qbk b/doc/lexical_cast.qbk index 2006405..11f119d 100644 --- a/doc/lexical_cast.qbk +++ b/doc/lexical_cast.qbk @@ -68,7 +68,17 @@ Library features defined in [@boost:boost/lexical_cast.hpp boost/lexical_cast.hp template Target lexical_cast(const AnyCharacterType* chars, std::size_t count); - } + + namespace conversion + { + template + bool try_lexical_convert(Source&& arg, Target& result); + + template + bool try_lexical_convert(const AnyCharacterType* chars, std::size_t count, Target& result); + + } // namespace conversion + } // namespace boost `` [section lexical_cast] @@ -122,6 +132,38 @@ Where a higher degree of control is required over conversions, `std::stringstrea Exception used to indicate runtime lexical_cast failure. [endsect] +[section try_lexical_convert] +`boost::lexical_cast` remains the main interface for lexical conversions. It must be used by default in most cases. However +some developers wish to make their own conversion functions, reusing all the optimizations of `boost::lexical_cast`. That's +where the `boost::conversion::try_lexical_convert` function steps in. + +`try_lexical_convert` returns `true` if conversion succeeded, otherwise returns `false`. If conversion failed and `false` was returned, +state of `result` output variable is undefined. + +Actually, `boost::lexical_cast` is implemented using `try_lexical_convert`: +`` + template + inline Target lexical_cast(const Source &arg) + { + Target result; + + if (!conversion::try_lexical_convert(arg, result)) + throw bad_lexical_cast(); + + return result; + } +`` + +`try_lexical_convert` relaxes the requirements for `Target` type. `Target` must not be CopyConstructible or DefaultConstructible. + +Following requirements for `Target` and `Source` remain: + +* Source must be OutputStreamable, meaning that an `operator<<` is defined that takes a `std::ostream` or `std::wostream` object on the left hand side and an instance of the argument type on the right. +* Target must be InputStreamable, meaning that an `operator>>` is defined that takes a `std::istream` or `std::wistream` object on the left hand side and an instance of the result type on the right. + +[endsect] + + [endsect] [section Frequently Asked Questions] @@ -194,6 +236,10 @@ limitation of compiler options that you use. [section Changes] +* [*boost 1.56.0 :] + + * Added `try_lexical_convert` functions. + * [*boost 1.54.0 :] * Fix some issues with `boost::int128_type` and `boost::uint128_type` conversions. Notify user at compile time From 36bc5aea445a0773d1f28ab8893f7b2ca7c467d4 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 3 Feb 2014 16:22:27 +0400 Subject: [PATCH 07/10] Attempt to fix access violation during forwarding of arrays on MSVC --- include/boost/lexical_cast.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index cf1d700..50af834 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -2096,7 +2096,8 @@ namespace boost { template struct copy_converter_impl { -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +// MSVC fail to forward an array. This error is reported to MSVC's developer. +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_MSVC) template static inline bool try_convert(T&& arg, Target& result) { result = static_cast(arg); // eqaul to `result = std::forward(arg);` @@ -2250,7 +2251,8 @@ namespace boost { namespace conversion { namespace detail { -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +// MSVC fail to forward an array. This error is reported to MSVC's developer. +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_MSVC) template inline bool try_lexical_convert(Source&& arg, Target& result) { From 5a757dcecea184cffc8ad5813a44f162096196fc Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Tue, 4 Feb 2014 13:13:33 +0400 Subject: [PATCH 08/10] Docs updated and minor fixes for MSVC2013 --- doc/lexical_cast.qbk | 4 ++-- include/boost/lexical_cast.hpp | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/lexical_cast.qbk b/doc/lexical_cast.qbk index 11f119d..dd7673b 100644 --- a/doc/lexical_cast.qbk +++ b/doc/lexical_cast.qbk @@ -3,7 +3,7 @@ [version 1.0] [copyright 2000-2005 Kevlin Henney] [copyright 2006-2010 Alexander Nasonov] - [copyright 2011-2013 Antony Polukhin] + [copyright 2011-2014 Antony Polukhin] [category String and text processing] [category Miscellaneous] [license @@ -238,7 +238,7 @@ limitation of compiler options that you use. * [*boost 1.56.0 :] - * Added `try_lexical_convert` functions. + * Added `boost::conversion::try_lexical_convert` functions. * [*boost 1.54.0 :] diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 50af834..ae47053 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -19,7 +19,7 @@ // Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov, // Alexander Nasonov, Antony Polukhin, Justin Viiret, Michael Hofmann, // Cheng Yang, Matthew Bradbury, David W. Birdsall, Pavel Korzh and other Boosters -// when: November 2000, March 2003, June 2005, June 2006, March 2011 - 2013 +// when: November 2000, March 2003, June 2005, June 2006, March 2011 - 2014 #include #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING) @@ -2096,8 +2096,9 @@ namespace boost { template struct copy_converter_impl { -// MSVC fail to forward an array. This error is reported to MSVC's developer. -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_MSVC) +// MSVC fail to forward an array (DevDiv#555157 "SILENT BAD CODEGEN triggered by perfect forwarding", +// fixed in 2013 RTM). +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined(BOOST_MSVC) || BOOST_MSVC >= 1800) template static inline bool try_convert(T&& arg, Target& result) { result = static_cast(arg); // eqaul to `result = std::forward(arg);` @@ -2251,8 +2252,9 @@ namespace boost { namespace conversion { namespace detail { -// MSVC fail to forward an array. This error is reported to MSVC's developer. -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_MSVC) +// MSVC fail to forward an array (DevDiv#555157 "SILENT BAD CODEGEN triggered by perfect forwarding", +// fixed in 2013 RTM). +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined(BOOST_MSVC) || BOOST_MSVC >= 1800) template inline bool try_lexical_convert(Source&& arg, Target& result) { @@ -2515,7 +2517,7 @@ namespace boost { // Copyright Kevlin Henney, 2000-2005. // Copyright Alexander Nasonov, 2006-2010. -// Copyright Antony Polukhin, 2011-2013. +// Copyright Antony Polukhin, 2011-2014. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at From 90b4286753aca34ae4547005863d21884e3f96d5 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Thu, 6 Feb 2014 11:22:40 +0400 Subject: [PATCH 09/10] Because of the errors in MSVC with fowarding and static_casting arrays it was decided to drop minor optimization and do not perfect forward source types in try_lexical_convert --- doc/lexical_cast.qbk | 13 ++++++------- include/boost/lexical_cast.hpp | 22 ++-------------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/doc/lexical_cast.qbk b/doc/lexical_cast.qbk index dd7673b..ad9b5f2 100644 --- a/doc/lexical_cast.qbk +++ b/doc/lexical_cast.qbk @@ -72,7 +72,7 @@ Library features defined in [@boost:boost/lexical_cast.hpp boost/lexical_cast.hp namespace conversion { template - bool try_lexical_convert(Source&& arg, Target& result); + bool try_lexical_convert(const Source& arg, Target& result); template bool try_lexical_convert(const AnyCharacterType* chars, std::size_t count, Target& result); @@ -134,11 +134,11 @@ Exception used to indicate runtime lexical_cast failure. [section try_lexical_convert] `boost::lexical_cast` remains the main interface for lexical conversions. It must be used by default in most cases. However -some developers wish to make their own conversion functions, reusing all the optimizations of `boost::lexical_cast`. That's -where the `boost::conversion::try_lexical_convert` function steps in. +some developers wish to make their own conversion functions, reusing all the optimizations of the `boost::lexical_cast`. +That's where the `boost::conversion::try_lexical_convert` function steps in. -`try_lexical_convert` returns `true` if conversion succeeded, otherwise returns `false`. If conversion failed and `false` was returned, -state of `result` output variable is undefined. +`try_lexical_convert` returns `true` if conversion succeeded, otherwise returns `false`. If conversion +failed and `false` was returned, state of `result` output variable is undefined. Actually, `boost::lexical_cast` is implemented using `try_lexical_convert`: `` @@ -154,8 +154,7 @@ Actually, `boost::lexical_cast` is implemented using `try_lexical_convert`: } `` -`try_lexical_convert` relaxes the requirements for `Target` type. `Target` must not be CopyConstructible or DefaultConstructible. - +`try_lexical_convert` relaxes the CopyConstructible and DefaultConstructible requirements for `Target` type. Following requirements for `Target` and `Source` remain: * Source must be OutputStreamable, meaning that an `operator<<` is defined that takes a `std::ostream` or `std::wostream` object on the left hand side and an instance of the argument type on the right. diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index ae47053..421b823 100644 --- a/include/boost/lexical_cast.hpp +++ b/include/boost/lexical_cast.hpp @@ -2077,9 +2077,6 @@ namespace boost { i_interpreter_type i_interpreter; // Disabling ADL, by directly specifying operators. - // - // For compilers with perfect forwarding `static_cast(arg)` is - // eqaul to `std::forward(arg)`. if (!(i_interpreter.operator <<(arg))) return false; @@ -2252,25 +2249,10 @@ namespace boost { namespace conversion { namespace detail { -// MSVC fail to forward an array (DevDiv#555157 "SILENT BAD CODEGEN triggered by perfect forwarding", -// fixed in 2013 RTM). -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined(BOOST_MSVC) || BOOST_MSVC >= 1800) - template - inline bool try_lexical_convert(Source&& arg, Target& result) - { - typedef BOOST_DEDUCED_TYPENAME boost::remove_const< - BOOST_DEDUCED_TYPENAME boost::remove_reference::type - >::type no_cr_source_t; - - typedef Source&& forward_type; -#else template inline bool try_lexical_convert(const Source& arg, Target& result) { - typedef Source no_cr_source_t; - typedef const Source& forward_type; -#endif - typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay::type src; + 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, @@ -2302,7 +2284,7 @@ namespace boost { typedef BOOST_DEDUCED_TYPENAME caster_type_lazy::type caster_type; - return caster_type::try_convert(static_cast(arg), result); + return caster_type::try_convert(arg, result); } template From 1e65265b8f266c2755124a32fefc32e5011df9a0 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Tue, 11 Feb 2014 12:13:17 +0400 Subject: [PATCH 10/10] Minor fixes in tests --- test/lexical_cast_try_lexical_convert.cpp | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/lexical_cast_try_lexical_convert.cpp b/test/lexical_cast_try_lexical_convert.cpp index 0c7c4de..1c795c2 100644 --- a/test/lexical_cast_try_lexical_convert.cpp +++ b/test/lexical_cast_try_lexical_convert.cpp @@ -13,63 +13,63 @@ #include #include -using namespace boost; +using namespace boost::conversion; void try_uncommon_cases() { std::string sres; - const bool res1 = conversion::try_lexical_convert(std::string("Test string"), sres); + const bool res1 = try_lexical_convert(std::string("Test string"), sres); BOOST_CHECK(res1); BOOST_CHECK_EQUAL(sres, "Test string"); volatile int vires; - const bool res2 = conversion::try_lexical_convert(100, vires); + const bool res2 = try_lexical_convert(100, vires); BOOST_CHECK(res2); BOOST_CHECK_EQUAL(vires, 100); - const bool res3 = conversion::try_lexical_convert("Test string", sres); + const bool res3 = try_lexical_convert("Test string", sres); BOOST_CHECK(res3); BOOST_CHECK_EQUAL(sres, "Test string"); - const bool res4 = conversion::try_lexical_convert("Test string", sizeof("Test string") - 1, sres); + const bool res4 = try_lexical_convert("Test string", sizeof("Test string") - 1, sres); BOOST_CHECK(res4); BOOST_CHECK_EQUAL(sres, "Test string"); int ires; - BOOST_CHECK(!conversion::try_lexical_convert("Test string", ires)); - BOOST_CHECK(!conversion::try_lexical_convert(1.1, ires)); - BOOST_CHECK(!conversion::try_lexical_convert(-1.9, ires)); - BOOST_CHECK(!conversion::try_lexical_convert("1.1", ires)); - BOOST_CHECK(!conversion::try_lexical_convert("1000000000000000000000000000000000000000", ires)); + BOOST_CHECK(!try_lexical_convert("Test string", ires)); + BOOST_CHECK(!try_lexical_convert(1.1, ires)); + BOOST_CHECK(!try_lexical_convert(-1.9, ires)); + BOOST_CHECK(!try_lexical_convert("1.1", ires)); + BOOST_CHECK(!try_lexical_convert("1000000000000000000000000000000000000000", ires)); } void try_common_cases() { int ires = 0; - const bool res1 = conversion::try_lexical_convert(std::string("100"), ires); + const bool res1 = try_lexical_convert(std::string("100"), ires); BOOST_CHECK(res1); BOOST_CHECK_EQUAL(ires, 100); ires = 0; - const bool res2 = conversion::try_lexical_convert("-100", ires); + const bool res2 = try_lexical_convert("-100", ires); BOOST_CHECK(res2); BOOST_CHECK_EQUAL(ires, -100); float fres = 1.0f; - const bool res3 = conversion::try_lexical_convert("0.0", fres); + const bool res3 = try_lexical_convert("0.0", fres); BOOST_CHECK(res3); BOOST_CHECK_EQUAL(fres, 0.0f); fres = 1.0f; - const bool res4 = conversion::try_lexical_convert("0.0", sizeof("0.0") - 1, fres); + const bool res4 = try_lexical_convert("0.0", sizeof("0.0") - 1, fres); BOOST_CHECK(res4); BOOST_CHECK_EQUAL(fres, 0.0f); } -unit_test::test_suite *init_unit_test_suite(int, char *[]) +boost::unit_test::test_suite *init_unit_test_suite(int, char *[]) { - unit_test::test_suite *suite = + boost::unit_test::test_suite *suite = BOOST_TEST_SUITE("Tests for try_lexical_convert"); suite->add(BOOST_TEST_CASE(&try_uncommon_cases)); suite->add(BOOST_TEST_CASE(&try_common_cases));