286 changes: 169 additions & 117 deletions include/boost/lexical_cast.hpp
Expand Up @@ -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 <boost/config.hpp>
#if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING)
Expand Down Expand Up @@ -1666,7 +1666,6 @@ namespace boost {
return ((*this) << reinterpret_cast<boost::array<C, N> const& >(input));
}
#endif

template <class InStreamable>
bool operator<<(const InStreamable& input) { return shl_input_streamable(input); }
};
Expand Down Expand Up @@ -2058,125 +2057,148 @@ namespace boost {
{};

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 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<Source, Target> 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 <typename Source>
struct lexical_cast_copy
template <typename Target, typename Source>
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 <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
{
typedef boost::numeric::Trunc<Source> Rounder;
typedef Source source_type ;

typedef BOOST_DEDUCED_TYPENAME mpl::if_<
boost::is_arithmetic<Source>, 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<source_type>::epsilon();
typedef Source source_type;
typedef boost::numeric::Trunc<Source> Rounder;
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 {
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<source_type>::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;
};

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 ;

typedef typename Rounder::round_style round_style;
} ;
static inline source_type nearbyint(argument_type s, bool& /*is_ok*/) BOOST_NOEXCEPT {
return s;
}
};

template <class Source, class Target >
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 <typename Target, typename Source>
inline bool noexcept_numeric_convert(const Source& arg, Target& result) BOOST_NOEXCEPT {
typedef boost::numeric::converter<
Target,
Source,
boost::numeric::conversion_traits<Target, Source >,
nothrow_overflow_handler,
detect_precision_loss<Source >
> 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 Target lexical_cast_impl(const Source &arg)
{
return boost::numeric::converter<
Target,
Source,
boost::numeric::conversion_traits<Target,Source>,
nothrow_overflow_handler<Source, Target>,
detect_precision_loss<Source, Target>
>::convert(arg);
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>
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<Source>::value,
boost::mpl::identity<Source>,
boost::make_unsigned<Source>
>::type usource_t;

typedef boost::numeric::converter<
Target,
usource_t,
boost::numeric::conversion_traits<Target,usource_t>,
nothrow_overflow_handler<usource_t, Target>,
detect_precision_loss<usource_t, Target>
> converter_t;

return (
arg < 0 ? static_cast<Target>(0u - converter_t::convert(0u - arg)) : converter_t::convert(arg)
);

if (arg < 0) {
const bool res = noexcept_numeric_convert<Target, usource_t>(0u - arg, result);
result = static_cast<Target>(0u - result);
return res;
} else {
return noexcept_numeric_convert<Target, usource_t>(arg, result);
}
}
};

Expand All @@ -2199,10 +2221,9 @@ namespace boost {
* and the result will be the two's complement.
*/
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<
boost::type_traits::ice_and<
boost::is_unsigned<Target>::value,
Expand All @@ -2221,47 +2242,79 @@ namespace boost {
lexical_cast_dynamic_num_not_ignoring_minus<Target, Source>
>::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>
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::type_traits::ice_or<
boost::detail::is_xchar_to_xchar<Target, src >::value,
boost::detail::is_char_array_to_stdstring<Target, src >::value,
boost::type_traits::ice_and<
boost::is_same<Target, src >::value,
boost::detail::is_stdstring<Target >::value
>::value,
boost::type_traits::ice_and<
boost::is_same<Target, src >::value,
boost::detail::is_character<Target >::value
>::value
> shall_we_copy_t;

typedef boost::detail::is_arithmetic_and_not_xchars<Target, src >
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::detail::copy_converter_impl<Target, src > >,
boost::mpl::if_<
shall_we_copy_with_dynamic_check_t,
boost::detail::dynamic_num_converter_impl<Target, src >,
boost::detail::lexical_converter_impl<Target, src >
>
>::type caster_type_lazy;

typedef BOOST_DEDUCED_TYPENAME caster_type_lazy::type caster_type;

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)
{
typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay<Source>::type src;

typedef BOOST_DEDUCED_TYPENAME boost::type_traits::ice_or<
boost::detail::is_xchar_to_xchar<Target, src >::value,
boost::detail::is_char_array_to_stdstring<Target, src >::value,
boost::type_traits::ice_and<
boost::is_same<Target, src >::value,
boost::detail::is_stdstring<Target >::value
>::value,
boost::type_traits::ice_and<
boost::is_same<Target, src >::value,
boost::detail::is_character<Target >::value
>::value
> shall_we_copy_t;

typedef boost::detail::is_arithmetic_and_not_xchars<Target, src >
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::detail::lexical_cast_copy<src > >,
boost::mpl::if_<
shall_we_copy_with_dynamic_check_t,
boost::detail::lexical_cast_dynamic_num<Target, src >,
boost::detail::lexical_cast_do_cast<Target, src >
>
>::type caster_type_lazy;
Target result;

typedef BOOST_DEDUCED_TYPENAME caster_type_lazy::type caster_type;
if (!boost::conversion::detail::try_lexical_convert(arg, result))
BOOST_LCAST_THROW_BAD_CAST(Source, Target);

return caster_type::lexical_cast_impl(arg);
return result;
}

template <typename Target>
Expand All @@ -2272,13 +2325,12 @@ namespace boost {
);
}


template <typename Target>
inline Target lexical_cast(const unsigned char* chars, std::size_t count)
{
return ::boost::lexical_cast<Target>(
return ::boost::lexical_cast<Target>(
::boost::iterator_range<const unsigned char*>(chars, chars + count)
);
);
}

template <typename Target>
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile.v2
Expand Up @@ -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/<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.
Expand Down
8 changes: 4 additions & 4 deletions test/lexical_cast_float_types_test.cpp
Expand Up @@ -111,17 +111,17 @@ 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<test_t>(#VAL); \
BOOST_CHECK_CLOSE_FRACTION( (VAL ## L? VAL ## L : std::numeric_limits<test_t>::epsilon()), \
(converted_val ? converted_val : 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()),\
std::numeric_limits<test_t>::epsilon() \
); \
BOOST_CHECK_EQUAL(converted_val, lexical_cast<test_t>(L## #VAL) );

#else
#define CHECK_CLOSE_ABS_DIFF(VAL,TYPE) \
converted_val = lexical_cast<test_t>(#VAL); \
BOOST_CHECK_CLOSE_FRACTION( (VAL ## L? VAL ## L : std::numeric_limits<test_t>::epsilon()), \
(converted_val ? converted_val : 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()),\
std::numeric_limits<test_t>::epsilon() \
);
#endif
Expand Down
78 changes: 78 additions & 0 deletions 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 <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;
}