// Copyright Kevlin Henney, 2000-2005. // Copyright Alexander Nasonov, 2006-2010. // Copyright Antony Polukhin, 2011-2024. // // 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) // // what: lexical_cast custom keyword cast // who: contributed by Kevlin Henney, // enhanced with contributions from Terje Slettebo, // with additional fixes and suggestions from Gennaro Prota, // 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 - 2016 #ifndef BOOST_LEXICAL_CAST_DETAIL_CONVERTER_NUMERIC_HPP #define BOOST_LEXICAL_CAST_DETAIL_CONVERTER_NUMERIC_HPP #include #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif #include #include #include #include #include #include #include #include #include namespace boost { namespace detail { template bool ios_numeric_comparer_float(Source x, Source y) noexcept { return x == y || (boost::core::isnan(x) && boost::core::isnan(y)) || (x < (std::numeric_limits::min)()) ; } template constexpr bool is_out_of_range_for(T value) noexcept { return value > static_cast((std::numeric_limits::max)()) || value < static_cast((std::numeric_limits::min)()); } // integral -> integral template typename boost::enable_if_c< !boost::is_floating_point::value && !boost::is_floating_point::value, bool >::type noexcept_numeric_convert(Source arg, Target& result) noexcept { const Target target_tmp = static_cast(arg); const Source arg_restored = static_cast(target_tmp); if (arg == arg_restored) { result = target_tmp; return true; } return false; } // integral -> floating point template typename boost::enable_if_c< !boost::is_floating_point::value && boost::is_floating_point::value, bool >::type noexcept_numeric_convert(Source arg, Target& result) noexcept { const Target target_tmp = static_cast(arg); result = target_tmp; return true; } // floating point -> floating point template typename boost::enable_if_c< boost::is_floating_point::value && boost::is_floating_point::value, bool >::type noexcept_numeric_convert(Source arg, Target& result) noexcept { const Target target_tmp = static_cast(arg); const Source arg_restored = static_cast(target_tmp); if (detail::ios_numeric_comparer_float(arg, arg_restored)) { result = target_tmp; return true; } return false; } // floating point -> integral template typename boost::enable_if_c< boost::is_floating_point::value && !boost::is_floating_point::value, bool >::type noexcept_numeric_convert(Source arg, Target& result) noexcept { if (detail::is_out_of_range_for(arg)) { return false; } const Target target_tmp = static_cast(arg); const Source arg_restored = static_cast(target_tmp); if (detail::ios_numeric_comparer_float(arg, arg_restored)) { result = target_tmp; return true; } return false; } struct lexical_cast_dynamic_num_not_ignoring_minus { template static inline bool try_convert(Source arg, Target& result) noexcept { return boost::detail::noexcept_numeric_convert(arg, result); } }; struct lexical_cast_dynamic_num_ignoring_minus { template #if defined(__clang__) && (__clang_major__ > 3 || __clang_minor__ > 6) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif static inline bool try_convert(Source arg, Target& result) noexcept { typedef typename boost::conditional< boost::is_float::value, boost::type_identity, boost::make_unsigned >::type usource_lazy_t; typedef typename usource_lazy_t::type usource_t; if (arg < 0) { const bool res = boost::detail::noexcept_numeric_convert( static_cast(0u - static_cast(arg)), result ); result = static_cast(0u - result); return res; } else { return boost::detail::noexcept_numeric_convert(arg, result); } } }; /* * dynamic_num_converter_impl follows the rules: * 1) If Source can be converted to Target without precision loss and * without overflows, then assign Source to Target and return * * 2) If Source is less than 0 and Target is an unsigned integer, * then negate Source, check the requirements of rule 1) and if * successful, assign static_casted Source to Target and return * * 3) Otherwise throw a bad_lexical_cast exception * * * Rule 2) required because boost::lexical_cast has the behavior of * stringstream, which uses the rules of scanf for conversions. And * in the C99 standard for unsigned input value minus sign is * optional, so if a negative number is read, no errors will arise * and the result will be the two's complement. */ template struct dynamic_num_converter_impl { static inline bool try_convert(Source arg, Target& result) noexcept { typedef typename boost::conditional< boost::is_unsigned::value && (boost::is_signed::value || boost::is_float::value) && !(boost::is_same::value) && !(boost::is_same::value), lexical_cast_dynamic_num_ignoring_minus, lexical_cast_dynamic_num_not_ignoring_minus >::type caster_type; return caster_type::try_convert(arg, result); } }; }} // namespace boost::detail #endif // BOOST_LEXICAL_CAST_DETAIL_CONVERTER_NUMERIC_HPP