try_lexical_cast is now implemented in optimal way

This commit is contained in:
Antony Polukhin
2013-12-13 13:52:18 +04:00
parent 07082420b3
commit b40bc02f94
2 changed files with 118 additions and 126 deletions

View File

@@ -2058,9 +2058,9 @@ namespace boost {
{}; {};
template<typename Target, typename Source> template<typename Target, typename Source>
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<Source, Target> stream_trait; typedef lexical_cast_stream_traits<Source, Target> stream_trait;
@@ -2090,90 +2090,94 @@ namespace boost {
return true; 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 <typename Target, typename Source> template <typename Target, typename Source>
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; result = arg;
return true; return true;
} }
};
static inline const Source& lexical_cast_impl(const Source &arg) BOOST_NOEXCEPT template <class Source >
{ struct detect_precision_loss
return arg; {
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);
}
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 ;
static inline source_type nearbyint(argument_type s, bool& /*is_ok*/) BOOST_NOEXCEPT {
return s;
} }
}; };
template <class Source, class Target >
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();
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 <class Source, class Target >
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 <class Converter, typename Target, typename Source>
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<Source >, Converter >::value,
Converter,
fake_precision_loss<Converter, 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> template <typename Target, typename Source>
struct lexical_cast_dynamic_num_not_ignoring_minus 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<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); > >(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,
@@ -2184,14 +2188,18 @@ namespace boost {
typedef boost::numeric::converter< typedef boost::numeric::converter<
Target, Target,
usource_t, usource_t,
boost::numeric::conversion_traits<Target,usource_t>, boost::numeric::conversion_traits<Target, usource_t>,
nothrow_overflow_handler<usource_t, Target>, nothrow_overflow_handler,
detect_precision_loss<usource_t, Target> detect_precision_loss<usource_t >
> converter_t; > converter_t;
return ( if (arg < 0) {
arg < 0 ? static_cast<Target>(0u - converter_t::convert(0u - arg)) : converter_t::convert(arg) const bool res = stateful_numeric_convert<converter_t>(0u - arg, result);
); result = static_cast<Target>(0u - result);
return res;
} else {
return stateful_numeric_convert<converter_t>(arg, result);
}
} }
}; };
@@ -2214,20 +2222,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 bool try_lexical_cast_impl(const Source& arg, Target& result) static inline bool try_convert(const Source &arg, Target& result) BOOST_NOEXCEPT
{
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< typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c<
boost::type_traits::ice_and< boost::type_traits::ice_and<
@@ -2247,64 +2244,59 @@ 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);
} }
}; };
}
template <typename Target, typename Source>
struct lexical_cast_caster_type_detector {
typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay<Source>::type src;
typedef BOOST_DEDUCED_TYPENAME boost::type_traits::ice_or< template <typename Target, typename Source>
boost::detail::is_xchar_to_xchar<Target, src >::value, inline bool try_lexical_cast(const Source &arg, Target& result)
boost::detail::is_char_array_to_stdstring<Target, src >::value, {
boost::type_traits::ice_and< typedef BOOST_DEDUCED_TYPENAME boost::detail::array_to_pointer_decay<Source>::type src;
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 > typedef BOOST_DEDUCED_TYPENAME boost::type_traits::ice_or<
shall_we_copy_with_dynamic_check_t; 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;
// We do evaluate second `if_` lazily to avoid unnecessary instantiations typedef boost::detail::is_arithmetic_and_not_xchars<Target, src >
// of `shall_we_copy_with_dynamic_check_t` and improve compilation times. shall_we_copy_with_dynamic_check_t;
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c<
shall_we_copy_t::value,
boost::mpl::identity<boost::detail::lexical_cast_copy<Target, 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;
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::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 Source> template <typename Target, typename Source>
inline Target lexical_cast(const Source &arg) inline Target lexical_cast(const Source &arg)
{ {
typedef BOOST_DEDUCED_TYPENAME Target result;
boost::detail::lexical_cast_caster_type_detector<Target, Source>
::caster_type caster_type;
return caster_type::lexical_cast_impl(arg); if (!try_lexical_cast(arg, result))
} BOOST_LCAST_THROW_BAD_CAST(Source, Target);
template <typename Target, typename Source> return result;
inline bool try_lexical_cast(const Source &arg, Target& result)
{
typedef BOOST_DEDUCED_TYPENAME
boost::detail::lexical_cast_caster_type_detector<Target, Source>
::caster_type caster_type;
return caster_type::try_lexical_cast_impl(arg, result);
} }
template <typename Target> template <typename Target>

View File

@@ -111,8 +111,8 @@ 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() \
); \ ); \
BOOST_CHECK_EQUAL(converted_val, lexical_cast<test_t>(L## #VAL) ); BOOST_CHECK_EQUAL(converted_val, lexical_cast<test_t>(L## #VAL) );
@@ -120,8 +120,8 @@ 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() \
); );
#endif #endif