Merge branch 'develop'

This commit is contained in:
Antony Polukhin
2014-03-04 16:37:10 +04:00
5 changed files with 297 additions and 121 deletions

View File

@@ -3,7 +3,7 @@
[version 1.0] [version 1.0]
[copyright 2000-2005 Kevlin Henney] [copyright 2000-2005 Kevlin Henney]
[copyright 2006-2010 Alexander Nasonov] [copyright 2006-2010 Alexander Nasonov]
[copyright 2011-2013 Antony Polukhin] [copyright 2011-2014 Antony Polukhin]
[category String and text processing] [category String and text processing]
[category Miscellaneous] [category Miscellaneous]
[license [license
@@ -68,7 +68,17 @@ Library features defined in [@boost:boost/lexical_cast.hpp boost/lexical_cast.hp
template <typename Target> template <typename Target>
Target lexical_cast(const AnyCharacterType* chars, std::size_t count); Target lexical_cast(const AnyCharacterType* chars, std::size_t count);
}
namespace conversion
{
template<typename Target, typename Source>
bool try_lexical_convert(const Source& arg, Target& result);
template <typename AnyCharacterType, typename Target>
bool try_lexical_convert(const AnyCharacterType* chars, std::size_t count, Target& result);
} // namespace conversion
} // namespace boost
`` ``
[section lexical_cast] [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. Exception used to indicate runtime lexical_cast failure.
[endsect] [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 <typename Target, typename Source>
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] [endsect]
[section Frequently Asked Questions] [section Frequently Asked Questions]
@@ -194,6 +235,10 @@ limitation of compiler options that you use.
[section Changes] [section Changes]
* [*boost 1.56.0 :]
* Added `boost::conversion::try_lexical_convert` functions.
* [*boost 1.54.0 :] * [*boost 1.54.0 :]
* Fix some issues with `boost::int128_type` and `boost::uint128_type` conversions. Notify user at compile time * Fix some issues with `boost::int128_type` and `boost::uint128_type` conversions. Notify user at compile time

View File

@@ -19,7 +19,7 @@
// Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov, // Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov,
// Alexander Nasonov, Antony Polukhin, Justin Viiret, Michael Hofmann, // Alexander Nasonov, Antony Polukhin, Justin Viiret, Michael Hofmann,
// Cheng Yang, Matthew Bradbury, David W. Birdsall, Pavel Korzh and other Boosters // 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 <boost/config.hpp> #include <boost/config.hpp>
#if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING) #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING)
@@ -1666,7 +1666,6 @@ namespace boost {
return ((*this) << reinterpret_cast<boost::array<C, N> const& >(input)); return ((*this) << reinterpret_cast<boost::array<C, N> const& >(input));
} }
#endif #endif
template <class InStreamable> template <class InStreamable>
bool operator<<(const InStreamable& input) { return shl_input_streamable(input); } bool operator<<(const InStreamable& input) { return shl_input_streamable(input); }
}; };
@@ -2058,9 +2057,7 @@ namespace boost {
{}; {};
template<typename Target, typename Source> template<typename Target, typename Source>
struct lexical_cast_do_cast struct lexical_converter_impl
{
static inline Target lexical_cast_impl(const Source& arg)
{ {
typedef lexical_cast_stream_traits<Source, Target> stream_trait; typedef lexical_cast_stream_traits<Source, Target> stream_trait;
@@ -2076,52 +2073,58 @@ namespace boost {
BOOST_DEDUCED_TYPENAME stream_trait::traits BOOST_DEDUCED_TYPENAME stream_trait::traits
> o_interpreter_type; > o_interpreter_type;
// Target type must be default constructible static inline bool try_convert(const Source& arg, Target& result) {
Target result;
i_interpreter_type i_interpreter; i_interpreter_type i_interpreter;
// Disabling ADL, by directly specifying operators. // 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()); o_interpreter_type out(i_interpreter.cbegin(), i_interpreter.cend());
// Disabling ADL, by directly specifying operators. // Disabling ADL, by directly specifying operators.
if(!input_ok || !(out.operator >>(result))) if(!(out.operator >>(result)))
BOOST_LCAST_THROW_BAD_CAST(Source, Target); return false;
return result; return true;
} }
}; };
template <typename Source> template <typename Target, typename Source>
struct lexical_cast_copy struct copy_converter_impl
{ {
static inline const Source& lexical_cast_impl(const Source &arg) BOOST_NOEXCEPT // MSVC fail to forward an array (DevDiv#555157 "SILENT BAD CODEGEN triggered by perfect forwarding",
{ // fixed in 2013 RTM).
return arg; #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined(BOOST_MSVC) || BOOST_MSVC >= 1800)
template <class T>
static inline bool try_convert(T&& arg, Target& result) {
result = static_cast<T&&>(arg); // eqaul to `result = std::forward<T>(arg);`
return true;
} }
#else
static inline bool try_convert(const Source& arg, Target& result) {
result = arg;
return true;
}
#endif
}; };
template <class Source, class Target > template <class Source >
struct detect_precision_loss struct detect_precision_loss
{ {
typedef boost::numeric::Trunc<Source> Rounder;
typedef Source source_type; typedef Source source_type;
typedef boost::numeric::Trunc<Source> Rounder;
typedef BOOST_DEDUCED_TYPENAME mpl::if_< typedef BOOST_DEDUCED_TYPENAME mpl::if_<
boost::is_arithmetic<Source>, Source, Source const& boost::is_arithmetic<Source>, Source, Source const&
>::type argument_type ; >::type argument_type ;
static source_type nearbyint ( argument_type s ) static inline source_type nearbyint(argument_type s, bool& is_ok) BOOST_NOEXCEPT {
{
const source_type near_int = Rounder::nearbyint(s); const source_type near_int = Rounder::nearbyint(s);
if (near_int) { if (near_int && is_ok) {
const source_type orig_div_round = s / near_int; const source_type orig_div_round = s / near_int;
const source_type eps = std::numeric_limits<source_type>::epsilon(); const source_type eps = std::numeric_limits<source_type>::epsilon();
if ((orig_div_round > 1 ? orig_div_round - 1 : 1 - orig_div_round) > eps) is_ok = !((orig_div_round > 1 ? orig_div_round - 1 : 1 - orig_div_round) > eps);
BOOST_LCAST_THROW_BAD_CAST(Source, Target);
} }
return s; return s;
@@ -2130,53 +2133,72 @@ namespace boost {
typedef typename Rounder::round_style round_style; typedef typename Rounder::round_style round_style;
}; };
template <class Source, class Target > template <typename Base, class Source>
struct fake_precision_loss: public Base
{
typedef Source source_type ;
typedef BOOST_DEDUCED_TYPENAME mpl::if_<
boost::is_arithmetic<Source>, Source, Source const&
>::type argument_type ;
static inline source_type nearbyint(argument_type s, bool& /*is_ok*/) BOOST_NOEXCEPT {
return s;
}
};
struct nothrow_overflow_handler struct nothrow_overflow_handler
{ {
void operator() ( boost::numeric::range_check_result r ) inline bool operator() ( boost::numeric::range_check_result r ) const BOOST_NOEXCEPT {
{ return (r == boost::numeric::cInRange);
if (r != boost::numeric::cInRange)
BOOST_LCAST_THROW_BAD_CAST(Source, Target);
} }
}; };
template <typename Target, typename Source> template <typename Target, typename Source>
struct lexical_cast_dynamic_num_not_ignoring_minus inline bool noexcept_numeric_convert(const Source& arg, Target& result) BOOST_NOEXCEPT {
{ typedef boost::numeric::converter<
static inline Target lexical_cast_impl(const Source &arg)
{
return boost::numeric::converter<
Target, Target,
Source, Source,
boost::numeric::conversion_traits<Target, Source >, boost::numeric::conversion_traits<Target, Source >,
nothrow_overflow_handler<Source, Target>, nothrow_overflow_handler,
detect_precision_loss<Source, Target> detect_precision_loss<Source >
>::convert(arg); > converter_orig_t;
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c<
boost::is_base_of< detect_precision_loss<Source >, converter_orig_t >::value,
converter_orig_t,
fake_precision_loss<converter_orig_t, Source>
>::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 <typename Target, typename Source>
struct lexical_cast_dynamic_num_not_ignoring_minus
{
static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT {
return noexcept_numeric_convert<Target, Source >(arg, result);
} }
}; };
template <typename Target, typename Source> template <typename Target, typename Source>
struct lexical_cast_dynamic_num_ignoring_minus 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< typedef BOOST_DEDUCED_TYPENAME boost::mpl::eval_if_c<
boost::is_float<Source>::value, boost::is_float<Source>::value,
boost::mpl::identity<Source>, boost::mpl::identity<Source>,
boost::make_unsigned<Source> boost::make_unsigned<Source>
>::type usource_t; >::type usource_t;
typedef boost::numeric::converter< if (arg < 0) {
Target, const bool res = noexcept_numeric_convert<Target, usource_t>(0u - arg, result);
usource_t, result = static_cast<Target>(0u - result);
boost::numeric::conversion_traits<Target,usource_t>, return res;
nothrow_overflow_handler<usource_t, Target>, } else {
detect_precision_loss<usource_t, Target> return noexcept_numeric_convert<Target, usource_t>(arg, result);
> converter_t; }
return (
arg < 0 ? static_cast<Target>(0u - converter_t::convert(0u - arg)) : converter_t::convert(arg)
);
} }
}; };
@@ -2199,10 +2221,9 @@ namespace boost {
* and the result will be the two's complement. * and the result will be the two's complement.
*/ */
template <typename Target, typename Source> template <typename Target, typename Source>
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< typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c<
boost::type_traits::ice_and< boost::type_traits::ice_and<
boost::is_unsigned<Target>::value, boost::is_unsigned<Target>::value,
@@ -2221,13 +2242,15 @@ namespace boost {
lexical_cast_dynamic_num_not_ignoring_minus<Target, Source> lexical_cast_dynamic_num_not_ignoring_minus<Target, Source>
>::type caster_type; >::type caster_type;
return caster_type::lexical_cast_impl(arg); return caster_type::try_convert(arg, result);
} }
}; };
} }
namespace conversion { namespace detail {
template <typename Target, typename Source> template <typename Target, typename Source>
inline Target lexical_cast(const Source &arg) inline bool try_lexical_convert(const Source& arg, Target& result)
{ {
typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay<Source>::type src; typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay<Source>::type src;
@@ -2251,17 +2274,47 @@ namespace boost {
// of `shall_we_copy_with_dynamic_check_t` and improve compilation times. // of `shall_we_copy_with_dynamic_check_t` and improve compilation times.
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c<
shall_we_copy_t::value, shall_we_copy_t::value,
boost::mpl::identity<boost::detail::lexical_cast_copy<src > >, boost::mpl::identity<boost::detail::copy_converter_impl<Target, src > >,
boost::mpl::if_< boost::mpl::if_<
shall_we_copy_with_dynamic_check_t, shall_we_copy_with_dynamic_check_t,
boost::detail::lexical_cast_dynamic_num<Target, src >, boost::detail::dynamic_num_converter_impl<Target, src >,
boost::detail::lexical_cast_do_cast<Target, src > boost::detail::lexical_converter_impl<Target, src >
> >
>::type caster_type_lazy; >::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::lexical_cast_impl(arg); return caster_type::try_convert(arg, result);
}
template <typename Target, typename CharacterT>
inline bool try_lexical_convert(const CharacterT* chars, std::size_t count, Target& result)
{
BOOST_STATIC_ASSERT_MSG(
boost::detail::is_character<CharacterT>::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<const CharacterT*>(chars, chars + count), result
);
}
}} // namespace conversion::detail
namespace conversion {
// ADL barrier
using ::boost::conversion::detail::try_lexical_convert;
}
template <typename Target, typename Source>
inline Target lexical_cast(const Source &arg)
{
Target result;
if (!boost::conversion::detail::try_lexical_convert(arg, result))
BOOST_LCAST_THROW_BAD_CAST(Source, Target);
return result;
} }
template <typename Target> template <typename Target>
@@ -2272,7 +2325,6 @@ namespace boost {
); );
} }
template <typename Target> template <typename Target>
inline Target lexical_cast(const unsigned char* chars, std::size_t count) inline Target lexical_cast(const unsigned char* chars, std::size_t count)
{ {
@@ -2447,7 +2499,7 @@ namespace boost {
// Copyright Kevlin Henney, 2000-2005. // Copyright Kevlin Henney, 2000-2005.
// Copyright Alexander Nasonov, 2006-2010. // 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 // Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at // accompanying file LICENSE_1_0.txt or copy at

View File

@@ -62,6 +62,7 @@ test-suite conversion
[ run lexical_cast_stream_traits_test.cpp ] [ run lexical_cast_stream_traits_test.cpp ]
[ compile-fail lexical_cast_to_pointer_test.cpp ] [ compile-fail lexical_cast_to_pointer_test.cpp ]
[ run lexical_cast_filesystem_test.cpp ../../filesystem/build//boost_filesystem/<link>static ] [ run lexical_cast_filesystem_test.cpp ../../filesystem/build//boost_filesystem/<link>static ]
[ run lexical_cast_try_lexical_convert.cpp ]
; ;
# Assuring that examples compile and run. Adding sources from `example` directory to the `conversion` test suite. # Assuring that examples compile and run. Adding sources from `example` directory to the `conversion` test suite.

View File

@@ -111,7 +111,7 @@ void test_conversion_from_to_float_for_locale()
#ifndef BOOST_LCAST_NO_WCHAR_T #ifndef BOOST_LCAST_NO_WCHAR_T
#define CHECK_CLOSE_ABS_DIFF(VAL,PREFIX) \ #define CHECK_CLOSE_ABS_DIFF(VAL,PREFIX) \
converted_val = lexical_cast<test_t>(#VAL); \ converted_val = lexical_cast<test_t>(#VAL); \
BOOST_CHECK_CLOSE_FRACTION( (VAL ## L? VAL ## L : std::numeric_limits<test_t>::epsilon()), \ BOOST_CHECK_CLOSE_FRACTION( (static_cast<bool>(VAL ## L)? VAL ## L : std::numeric_limits<test_t>::epsilon()), \
(converted_val ? converted_val : std::numeric_limits<test_t>::epsilon()),\ (converted_val ? converted_val : std::numeric_limits<test_t>::epsilon()),\
std::numeric_limits<test_t>::epsilon() \ std::numeric_limits<test_t>::epsilon() \
); \ ); \
@@ -120,7 +120,7 @@ void test_conversion_from_to_float_for_locale()
#else #else
#define CHECK_CLOSE_ABS_DIFF(VAL,TYPE) \ #define CHECK_CLOSE_ABS_DIFF(VAL,TYPE) \
converted_val = lexical_cast<test_t>(#VAL); \ converted_val = lexical_cast<test_t>(#VAL); \
BOOST_CHECK_CLOSE_FRACTION( (VAL ## L? VAL ## L : std::numeric_limits<test_t>::epsilon()), \ BOOST_CHECK_CLOSE_FRACTION( (static_cast<bool>(VAL ## L)? VAL ## L : std::numeric_limits<test_t>::epsilon()), \
(converted_val ? converted_val : std::numeric_limits<test_t>::epsilon()),\ (converted_val ? converted_val : std::numeric_limits<test_t>::epsilon()),\
std::numeric_limits<test_t>::epsilon() \ std::numeric_limits<test_t>::epsilon() \
); );

View File

@@ -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 <boost/config.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/test/unit_test.hpp>
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;
}