// Copyright Kevlin Henney, 2000-2005. // Copyright Alexander Nasonov, 2006-2010. // Copyright Antony Polukhin, 2011-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) // // 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 - 2014 #ifndef BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP #define BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP #include #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_NO_STD_LOCALE # include #else # ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE // Getting error at this point means, that your STL library is old/lame/misconfigured. // If nothing can be done with STL library, define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE, // but beware: lexical_cast will understand only 'C' locale delimeters and thousands // separators. # error "Unable to use header. Define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE to force " # error "boost::lexical_cast to use only 'C' locale during conversions." # endif #endif #include #include #include #include namespace boost { namespace detail // lcast_to_unsigned { template inline BOOST_DEDUCED_TYPENAME boost::make_unsigned::type lcast_to_unsigned(const T value) BOOST_NOEXCEPT { typedef BOOST_DEDUCED_TYPENAME boost::make_unsigned::type result_type; return value < 0 ? static_cast(0u - static_cast(value)) : static_cast(value); } } namespace detail // lcast_put_unsigned { template class lcast_put_unsigned: boost::noncopyable { typedef BOOST_DEDUCED_TYPENAME Traits::int_type int_type; BOOST_DEDUCED_TYPENAME boost::mpl::if_c< (sizeof(int_type) > sizeof(T)) , int_type , T >::type m_value; CharT* m_finish; CharT const m_czero; int_type const m_zero; public: lcast_put_unsigned(const T n_param, CharT* finish) BOOST_NOEXCEPT : m_value(n_param), m_finish(finish) , m_czero(lcast_char_constants::zero), m_zero(Traits::to_int_type(m_czero)) { #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS BOOST_STATIC_ASSERT(!std::numeric_limits::is_signed); #endif } CharT* convert() { #ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE std::locale loc; if (loc == std::locale::classic()) { return main_convert_loop(); } typedef std::numpunct numpunct; numpunct const& np = BOOST_USE_FACET(numpunct, loc); std::string const grouping = np.grouping(); std::string::size_type const grouping_size = grouping.size(); if (!grouping_size || grouping[0] <= 0) { return main_convert_loop(); } #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS // Check that ulimited group is unreachable: BOOST_STATIC_ASSERT(std::numeric_limits::digits10 < CHAR_MAX); #endif CharT const thousands_sep = np.thousands_sep(); std::string::size_type group = 0; // current group number char last_grp_size = grouping[0]; char left = last_grp_size; do { if (left == 0) { ++group; if (group < grouping_size) { char const grp_size = grouping[group]; last_grp_size = (grp_size <= 0 ? static_cast(CHAR_MAX) : grp_size); } left = last_grp_size; --m_finish; Traits::assign(*m_finish, thousands_sep); } --left; } while (main_convert_iteration()); return m_finish; #else return main_convert_loop(); #endif } private: inline bool main_convert_iteration() BOOST_NOEXCEPT { --m_finish; int_type const digit = static_cast(m_value % 10U); Traits::assign(*m_finish, Traits::to_char_type(m_zero + digit)); m_value /= 10; return !!m_value; // supressing warnings } inline CharT* main_convert_loop() BOOST_NOEXCEPT { while (main_convert_iteration()); return m_finish; } }; } namespace detail // lcast_ret_unsigned { template class lcast_ret_unsigned: boost::noncopyable { bool m_multiplier_overflowed; T m_multiplier; T& m_value; const CharT* const m_begin; const CharT* m_end; public: lcast_ret_unsigned(T& value, const CharT* const begin, const CharT* end) BOOST_NOEXCEPT : m_multiplier_overflowed(false), m_multiplier(1), m_value(value), m_begin(begin), m_end(end) { #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS BOOST_STATIC_ASSERT(!std::numeric_limits::is_signed); // GCC when used with flag -std=c++0x may not have std::numeric_limits // specializations for __int128 and unsigned __int128 types. // Try compilation with -std=gnu++0x or -std=gnu++11. // // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40856 BOOST_STATIC_ASSERT_MSG(std::numeric_limits::is_specialized, "std::numeric_limits are not specialized for integral type passed to boost::lexical_cast" ); #endif } inline bool convert() { CharT const czero = lcast_char_constants::zero; --m_end; m_value = static_cast(0); if (m_begin > m_end || *m_end < czero || *m_end >= czero + 10) return false; m_value = static_cast(*m_end - czero); --m_end; #ifdef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE return main_convert_loop(); #else std::locale loc; if (loc == std::locale::classic()) { return main_convert_loop(); } typedef std::numpunct numpunct; numpunct const& np = BOOST_USE_FACET(numpunct, loc); std::string const& grouping = np.grouping(); std::string::size_type const grouping_size = grouping.size(); /* According to Programming languages - C++ * we MUST check for correct grouping */ if (!grouping_size || grouping[0] <= 0) { return main_convert_loop(); } unsigned char current_grouping = 0; CharT const thousands_sep = np.thousands_sep(); char remained = static_cast(grouping[current_grouping] - 1); for (;m_end >= m_begin; --m_end) { if (remained) { if (!main_convert_iteration()) { return false; } --remained; } else { if ( !Traits::eq(*m_end, thousands_sep) ) //|| begin == end ) return false; { /* * According to Programming languages - C++ * Digit grouping is checked. That is, the positions of discarded * separators is examined for consistency with * use_facet >(loc ).grouping() * * BUT what if there is no separators at all and grouping() * is not empty? Well, we have no extraced separators, so we * won`t check them for consistency. This will allow us to * work with "C" locale from other locales */ return main_convert_loop(); } else { if (m_begin == m_end) return false; if (current_grouping < grouping_size - 1) ++current_grouping; remained = grouping[current_grouping]; } } } /*for*/ return true; #endif } private: // Iteration that does not care about grouping/separators and assumes that all // input characters are digits inline bool main_convert_iteration() BOOST_NOEXCEPT { CharT const czero = lcast_char_constants::zero; T const maxv = (std::numeric_limits::max)(); m_multiplier_overflowed = m_multiplier_overflowed || (maxv/10 < m_multiplier); m_multiplier = static_cast(m_multiplier * 10); T const dig_value = static_cast(*m_end - czero); T const new_sub_value = static_cast(m_multiplier * dig_value); // We must correctly handle situations like `000000000000000000000000000001`. // So we take care of overflow only if `dig_value` is not '0'. if (*m_end < czero || *m_end >= czero + 10 // checking for correct digit || (dig_value && ( // checking for overflow of ... m_multiplier_overflowed // ... multiplier || static_cast(maxv / dig_value) < m_multiplier // ... subvalue || static_cast(maxv - new_sub_value) < m_value // ... whole expression )) ) return false; m_value = static_cast(m_value + new_sub_value); return true; } bool main_convert_loop() BOOST_NOEXCEPT { for ( ; m_end >= m_begin; --m_end) { if (!main_convert_iteration()) { return false; } } return true; } }; } } // namespace boost #endif // BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP