diff --git a/doc/lexical_cast.qbk b/doc/lexical_cast.qbk index 2006405..ad9b5f2 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 @@ -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(const 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,37 @@ 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 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. + +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 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. +* 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 +235,10 @@ limitation of compiler options that you use. [section Changes] +* [*boost 1.56.0 :] + + * Added `boost::conversion::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 diff --git a/include/boost/lexical_cast.hpp b/include/boost/lexical_cast.hpp index 5b43501..421b823 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) @@ -1666,7 +1666,6 @@ namespace boost { return ((*this) << reinterpret_cast const& >(input)); } #endif - template bool operator<<(const InStreamable& input) { return shl_input_streamable(input); } }; @@ -2058,125 +2057,148 @@ namespace boost { {}; template - struct lexical_cast_do_cast + struct lexical_converter_impl { - static inline Target lexical_cast_impl(const Source& arg) - { - 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; - // Target type must be default constructible - Target result; + typedef detail::lexical_ostream_limited_src< + BOOST_DEDUCED_TYPENAME stream_trait::char_type, + BOOST_DEDUCED_TYPENAME stream_trait::traits + > o_interpreter_type; + static inline bool try_convert(const Source& arg, 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 result; + return true; } }; - template - struct lexical_cast_copy + template + struct copy_converter_impl { - static inline const Source& lexical_cast_impl(const Source &arg) BOOST_NOEXCEPT - { - return arg; +// 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);` + return true; } +#else + static inline bool try_convert(const Source& arg, Target& result) { + result = arg; + return true; + } +#endif }; - template + template struct detect_precision_loss { - typedef boost::numeric::Trunc Rounder; - typedef Source source_type ; + typedef Source source_type; + typedef boost::numeric::Trunc Rounder; + typedef BOOST_DEDUCED_TYPENAME mpl::if_< + boost::is_arithmetic, Source, Source const& + >::type argument_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 { + 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(); - 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(); + is_ok = !((orig_div_round > 1 ? orig_div_round - 1 : 1 - orig_div_round) > eps); + } - if ((orig_div_round > 1 ? orig_div_round - 1 : 1 - orig_div_round) > eps) - BOOST_LCAST_THROW_BAD_CAST(Source, Target); + return s; } - return s ; - } + typedef typename Rounder::round_style round_style; + }; - 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 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 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_orig_t >::value, + converter_orig_t, + 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) - { - return boost::numeric::converter< - Target, - Source, - boost::numeric::conversion_traits, - nothrow_overflow_handler, - detect_precision_loss - >::convert(arg); + 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 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, 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; - - return ( - arg < 0 ? static_cast(0u - converter_t::convert(0u - arg)) : converter_t::convert(arg) - ); + + if (arg < 0) { + const bool res = noexcept_numeric_convert(0u - arg, result); + result = static_cast(0u - result); + return res; + } else { + return noexcept_numeric_convert(arg, result); + } } }; @@ -2199,10 +2221,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 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< boost::is_unsigned::value, @@ -2221,47 +2242,79 @@ 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); } }; } + namespace conversion { namespace detail { + + template + inline bool try_lexical_convert(const Source& arg, Target& result) + { + 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::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 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 inline Target lexical_cast(const Source &arg) { - typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay::type src; + Target result; - 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; + if (!boost::conversion::detail::try_lexical_convert(arg, result)) + BOOST_LCAST_THROW_BAD_CAST(Source, Target); - 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; - - return caster_type::lexical_cast_impl(arg); + return result; } template @@ -2272,13 +2325,12 @@ namespace boost { ); } - 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 @@ -2447,7 +2499,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 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_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 diff --git a/test/lexical_cast_try_lexical_convert.cpp b/test/lexical_cast_try_lexical_convert.cpp new file mode 100644 index 0000000..1c795c2 --- /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::conversion; + +void try_uncommon_cases() +{ + std::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 = try_lexical_convert(100, vires); + BOOST_CHECK(res2); + BOOST_CHECK_EQUAL(vires, 100); + + const bool res3 = try_lexical_convert("Test string", sres); + BOOST_CHECK(res3); + BOOST_CHECK_EQUAL(sres, "Test string"); + + 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(!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 = try_lexical_convert(std::string("100"), ires); + BOOST_CHECK(res1); + BOOST_CHECK_EQUAL(ires, 100); + + ires = 0; + const bool res2 = try_lexical_convert("-100", ires); + BOOST_CHECK(res2); + BOOST_CHECK_EQUAL(ires, -100); + + float fres = 1.0f; + const bool res3 = try_lexical_convert("0.0", fres); + BOOST_CHECK(res3); + BOOST_CHECK_EQUAL(fres, 0.0f); + + fres = 1.0f; + const bool res4 = try_lexical_convert("0.0", sizeof("0.0") - 1, fres); + BOOST_CHECK(res4); + BOOST_CHECK_EQUAL(fres, 0.0f); +} + +boost::unit_test::test_suite *init_unit_test_suite(int, char *[]) +{ + 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)); + + return suite; +}