// 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 - 2014, Nowember 2016 #ifndef BOOST_LEXICAL_CAST_DETAIL_CONVERTER_LEXICAL_STREAMS_HPP #define BOOST_LEXICAL_CAST_DETAIL_CONVERTER_LEXICAL_STREAMS_HPP #include #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_WSTRING) #define BOOST_LCAST_NO_WCHAR_T #endif #include #include #include #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 #ifdef BOOST_NO_STRINGSTREAM #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_NO_CWCHAR # include #endif // Forward declarations namespace boost { template class array; template class iterator_range; // forward declaration of boost::basic_string_view from Utility template class basic_string_view; } namespace boost { namespace detail { namespace lcast { template struct exact { static_assert(!boost::is_const::value, ""); static_assert(!boost::is_reference::value, ""); const T& payload; }; template< class CharT // a result of widest_char transformation , class Traits , std::size_t CharacterBufferSize > class optimized_src_stream { CharT buffer[CharacterBufferSize]; // After the `stream_in(` finishes, `[start, finish)` is // the range to output by `operator >>` const CharT* start; const CharT* finish; public: optimized_src_stream(optimized_src_stream&&) = delete; optimized_src_stream(const optimized_src_stream&) = delete; optimized_src_stream& operator=(optimized_src_stream&&) = delete; optimized_src_stream& operator=(const optimized_src_stream&) = delete; optimized_src_stream() noexcept : start(buffer) , finish(buffer + CharacterBufferSize) {} const CharT* cbegin() const noexcept { return start; } const CharT* cend() const noexcept { return finish; } private: bool shl_char(CharT ch) noexcept { Traits::assign(buffer[0], ch); finish = start + 1; return true; } #ifndef BOOST_LCAST_NO_WCHAR_T template bool shl_char(T ch) { static_assert(sizeof(T) <= sizeof(CharT), "boost::lexical_cast does not support narrowing of char types." "Use boost::locale instead" ); #ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE std::locale loc; CharT const w = BOOST_USE_FACET(std::ctype, loc).widen(ch); #else CharT const w = static_cast(ch); #endif Traits::assign(buffer[0], w); finish = start + 1; return true; } #endif bool shl_char_array(CharT const* str_value) noexcept { start = str_value; finish = start + Traits::length(str_value); return true; } bool shl_char_array_limited(CharT const* str, std::size_t max_size) noexcept { start = str; finish = start; const auto zero = Traits::to_char_type(0); while (finish < start + max_size && zero != *finish) { ++ finish; } return true; } template inline bool shl_unsigned(const T n) { CharT* tmp_finish = buffer + CharacterBufferSize; start = lcast_put_unsigned(n, tmp_finish).convert(); finish = tmp_finish; return true; } template inline bool shl_signed(const T n) { CharT* tmp_finish = buffer + CharacterBufferSize; typedef typename boost::make_unsigned::type utype; CharT* tmp_start = lcast_put_unsigned(lcast_to_unsigned(n), tmp_finish).convert(); if (n < 0) { --tmp_start; CharT const minus = lcast_char_constants::minus; Traits::assign(*tmp_start, minus); } start = tmp_start; finish = tmp_finish; return true; } bool shl_real_type(float val, char* begin) { const double val_as_double = val; finish = start + boost::core::snprintf(begin, CharacterBufferSize, "%.*g", static_cast(boost::detail::lcast_get_precision()), val_as_double); return finish > start; } bool shl_real_type(double val, char* begin) { finish = start + boost::core::snprintf(begin, CharacterBufferSize, "%.*g", static_cast(boost::detail::lcast_get_precision()), val); return finish > start; } #ifndef __MINGW32__ bool shl_real_type(long double val, char* begin) { finish = start + boost::core::snprintf(begin, CharacterBufferSize, "%.*Lg", static_cast(boost::detail::lcast_get_precision()), val ); return finish > start; } #else bool shl_real_type(long double val, char* begin) { return shl_real_type(static_cast(val), begin); } #endif #if !defined(BOOST_LCAST_NO_WCHAR_T) bool shl_real_type(float val, wchar_t* begin) { const double val_as_double = val; finish = start + boost::core::swprintf( begin, CharacterBufferSize, L"%.*g", static_cast(boost::detail::lcast_get_precision()), val_as_double ); return finish > start; } bool shl_real_type(double val, wchar_t* begin) { finish = start + boost::core::swprintf( begin, CharacterBufferSize, L"%.*g", static_cast(boost::detail::lcast_get_precision()), val ); return finish > start; } bool shl_real_type(long double val, wchar_t* begin) { finish = start + boost::core::swprintf( begin, CharacterBufferSize, L"%.*Lg", static_cast(boost::detail::lcast_get_precision()), val ); return finish > start; } #endif public: template using enable_if_compatible_char_t = typename boost::enable_if_c< boost::is_same::value || ( boost::is_same::value && ( boost::is_same::value || boost::is_same::value ) ), bool >::type; template bool stream_in(lcast::exact> x) noexcept { start = x.payload.data(); finish = start + x.payload.length(); return true; } template bool stream_in(lcast::exact> x) noexcept { start = x.payload.data(); finish = start + x.payload.length(); return true; } bool stream_in(lcast::exact x) noexcept { CharT const czero = lcast_char_constants::zero; Traits::assign(buffer[0], Traits::to_char_type(czero + x.payload)); finish = start + 1; return true; } bool stream_in(lcast::exact> x) noexcept { start = x.payload.begin; finish = x.payload.end; return true; } template enable_if_compatible_char_t stream_in(lcast::exact> x) noexcept { auto buf = boost::conversion::detail::make_buffer_view(x.payload.begin(), x.payload.end()); return stream_in(lcast::exact{buf}); } bool stream_in(lcast::exact x) { return shl_char(x.payload); } bool stream_in(lcast::exact x) { return shl_char(static_cast(x.payload)); } bool stream_in(lcast::exact x) { return shl_char(static_cast(x.payload)); } template typename boost::enable_if_c::value, bool>::type stream_in(lcast::exact x) { return shl_char(x.payload); } template enable_if_compatible_char_t stream_in(lcast::exact x) { return shl_char_array(reinterpret_cast(x.payload)); } template typename boost::enable_if_c::value && !boost::is_enum::value, bool>::type stream_in(lcast::exact x) { return shl_signed(x.payload); } template typename boost::enable_if_c::value && !boost::is_enum::value, bool>::type stream_in(lcast::exact x) { return shl_unsigned(x.payload); } template auto stream_in(lcast::exact x) -> decltype(shl_real_type(x.payload, buffer)) { const CharT* inf_nan = detail::get_inf_nan(x.payload, CharT()); if (inf_nan) { start = inf_nan; finish = start + Traits::length(inf_nan); return true; } return shl_real_type(x.payload, buffer); } template enable_if_compatible_char_t stream_in(lcast::exact> x) noexcept { return shl_char_array_limited(reinterpret_cast(x.payload.data()), N); } template enable_if_compatible_char_t stream_in(lcast::exact> x) noexcept { return shl_char_array_limited(reinterpret_cast(x.payload.data()), N); } #ifndef BOOST_NO_CXX17_HDR_STRING_VIEW template enable_if_compatible_char_t stream_in(lcast::exact> x) noexcept { start = reinterpret_cast(x.payload.data()); finish = start + x.payload.size(); return true; } #endif template enable_if_compatible_char_t stream_in(lcast::exact> x) noexcept { start = reinterpret_cast(x.payload.data()); finish = start + x.payload.size(); return true; } }; template class ios_src_stream { typedef detail::lcast::out_stream_t deduced_out_stream_t; typedef detail::lcast::stringbuffer_t deduced_out_buffer_t; deduced_out_buffer_t out_buffer; deduced_out_stream_t out_stream; const CharT* start = nullptr; const CharT* finish = nullptr; public: ios_src_stream(ios_src_stream&&) = delete; ios_src_stream(const ios_src_stream&) = delete; ios_src_stream& operator=(ios_src_stream&&) = delete; ios_src_stream& operator=(const ios_src_stream&) = delete; ios_src_stream(): out_buffer(), out_stream(&out_buffer) {} const CharT* cbegin() const noexcept { return start; } const CharT* cend() const noexcept { return finish; } private: const deduced_out_buffer_t* get_rdbuf() const { return static_cast( out_stream.rdbuf() ); } template bool shl_input_streamable(InputStreamable& input) { #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_LOCALE) // If you have compilation error at this point, than your STL library // does not support such conversions. Try updating it. static_assert(boost::is_same::value, ""); #endif #ifndef BOOST_NO_EXCEPTIONS out_stream.exceptions(std::ios::badbit); try { #endif bool const result = !(out_stream << input).fail(); const auto* const p = get_rdbuf(); start = p->pbase(); finish = p->pptr(); return result; #ifndef BOOST_NO_EXCEPTIONS } catch (const ::std::ios_base::failure& /*f*/) { return false; } #endif } template bool shl_char_array(T const* str_value) { static_assert(sizeof(T) <= sizeof(CharT), "boost::lexical_cast does not support narrowing of char types." "Use boost::locale instead" ); return shl_input_streamable(str_value); } template bool shl_real(T val) { const CharT* inf_nan = detail::get_inf_nan(val, CharT()); if (inf_nan) { start = inf_nan; finish = start + Traits::length(inf_nan); return true; } lcast_set_precision(out_stream, &val); return shl_input_streamable(val); } public: template typename boost::enable_if_c::value && sizeof(char) == sizeof(Type), bool>::type stream_in(lcast::exact x) { return shl_char_array(reinterpret_cast(x.payload)); } template typename boost::enable_if_c::value && sizeof(char) != sizeof(Type), bool>::type stream_in(lcast::exact x) { return shl_char_array(x.payload); } bool stream_in(lcast::exact x) { return shl_real(x.payload); } bool stream_in(lcast::exact x) { return shl_real(x.payload); } bool stream_in(lcast::exact x) { #ifndef __MINGW32__ return shl_real(x.payload); #else return shl_real(static_cast(x.payload)); #endif } template typename boost::enable_if_c::value, bool>::type stream_in(lcast::exact> x) noexcept { auto buf = boost::conversion::detail::make_buffer_view(x.payload.begin(), x.payload.end()); return stream_in(lcast::exact{buf}); } template typename boost::enable_if_c::value, bool>::type stream_in(lcast::exact> x) noexcept { auto buf = boost::conversion::detail::make_buffer_view(x.payload.begin(), x.payload.end()); return stream_in(lcast::exact{buf}); } template bool stream_in(lcast::exact x) { return shl_input_streamable(x.payload); } }; template class to_target_stream { //`[start, finish)` is the range to output by `operator >>` const CharT* start; const CharT* const finish; public: to_target_stream(to_target_stream&&) = delete; to_target_stream(const to_target_stream&) = delete; to_target_stream& operator=(to_target_stream&&) = delete; to_target_stream& operator=(const to_target_stream&) = delete; to_target_stream(const CharT* begin, const CharT* end) noexcept : start(begin) , finish(end) {} private: template #if defined(__clang__) && (__clang_major__ > 3 || __clang_minor__ > 6) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif bool shr_unsigned(Type& output) { if (start == finish) return false; CharT const minus = lcast_char_constants::minus; CharT const plus = lcast_char_constants::plus; bool const has_minus = Traits::eq(minus, *start); /* We won`t use `start' any more, so no need in decrementing it after */ if (has_minus || Traits::eq(plus, *start)) { ++start; } bool const succeed = lcast_ret_unsigned(output, start, finish).convert(); if (has_minus) { output = static_cast(0u - output); } return succeed; } template #if defined(__clang__) && (__clang_major__ > 3 || __clang_minor__ > 6) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif bool shr_signed(Type& output) { if (start == finish) return false; CharT const minus = lcast_char_constants::minus; CharT const plus = lcast_char_constants::plus; typedef typename make_unsigned::type utype; utype out_tmp = 0; bool const has_minus = Traits::eq(minus, *start); /* We won`t use `start' any more, so no need in decrementing it after */ if (has_minus || Traits::eq(plus, *start)) { ++start; } bool succeed = lcast_ret_unsigned(out_tmp, start, finish).convert(); if (has_minus) { utype const comp_val = (static_cast(1) << std::numeric_limits::digits); succeed = succeed && out_tmp<=comp_val; output = static_cast(0u - out_tmp); } else { utype const comp_val = static_cast((std::numeric_limits::max)()); succeed = succeed && out_tmp<=comp_val; output = static_cast(out_tmp); } return succeed; } template bool shr_using_base_class(InputStreamable& output) { static_assert( !boost::is_pointer::value, "boost::lexical_cast can not convert to pointers" ); #if defined(BOOST_NO_STRINGSTREAM) || defined(BOOST_NO_STD_LOCALE) static_assert(boost::is_same::value, "boost::lexical_cast can not convert, because your STL library does not " "support such conversions. Try updating it." ); #endif #if defined(BOOST_NO_STRINGSTREAM) std::istrstream stream(start, static_cast(finish - start)); #else typedef detail::lcast::buffer_t buffer_t; buffer_t buf; // Usually `istream` and `basic_istream` do not modify // content of buffer; `buffer_t` assures that this is true buf.setbuf(const_cast(start), static_cast(finish - start)); #if defined(BOOST_NO_STD_LOCALE) std::istream stream(&buf); #else std::basic_istream stream(&buf); #endif // BOOST_NO_STD_LOCALE #endif // BOOST_NO_STRINGSTREAM #ifndef BOOST_NO_EXCEPTIONS stream.exceptions(std::ios::badbit); try { #endif stream.unsetf(std::ios::skipws); lcast_set_precision(stream, static_cast(0)); return (stream >> output) && (stream.get() == Traits::eof()); #ifndef BOOST_NO_EXCEPTIONS } catch (const ::std::ios_base::failure& /*f*/) { return false; } #endif } template inline bool shr_xchar(T& output) noexcept { static_assert(sizeof(CharT) == sizeof(T), "boost::lexical_cast does not support narrowing of character types." "Use boost::locale instead" ); bool const ok = (finish - start == 1); if (ok) { CharT out; Traits::assign(out, *start); output = static_cast(out); } return ok; } template bool shr_std_array(ArrayT& output) noexcept { const std::size_t size = static_cast(finish - start); if (size > N - 1) { // `-1` because we need to store \0 at the end return false; } std::memcpy(&output[0], start, size * sizeof(CharT)); output[size] = Traits::to_char_type(0); return true; } public: bool stream_out(unsigned short& output) { return shr_unsigned(output); } bool stream_out(unsigned int& output) { return shr_unsigned(output); } bool stream_out(unsigned long int& output) { return shr_unsigned(output); } bool stream_out(short& output) { return shr_signed(output); } bool stream_out(int& output) { return shr_signed(output); } bool stream_out(long int& output) { return shr_signed(output); } #if defined(BOOST_HAS_LONG_LONG) bool stream_out(boost::ulong_long_type& output) { return shr_unsigned(output); } bool stream_out(boost::long_long_type& output) { return shr_signed(output); } #elif defined(BOOST_HAS_MS_INT64) bool stream_out(unsigned __int64& output) { return shr_unsigned(output); } bool stream_out(__int64& output) { return shr_signed(output); } #endif #ifdef BOOST_HAS_INT128 bool stream_out(boost::uint128_type& output) { return shr_unsigned(output); } bool stream_out(boost::int128_type& output) { return shr_signed(output); } #endif bool stream_out(char& output) { return shr_xchar(output); } bool stream_out(unsigned char& output) { return shr_xchar(output); } bool stream_out(signed char& output) { return shr_xchar(output); } #if !defined(BOOST_LCAST_NO_WCHAR_T) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) bool stream_out(wchar_t& output) { return shr_xchar(output); } #endif #if !defined(BOOST_NO_CXX11_CHAR16_T) && !defined(BOOST_NO_CXX11_UNICODE_LITERALS) bool stream_out(char16_t& output) { return shr_xchar(output); } #endif #if !defined(BOOST_NO_CXX11_CHAR32_T) && !defined(BOOST_NO_CXX11_UNICODE_LITERALS) bool stream_out(char32_t& output) { return shr_xchar(output); } #endif template bool stream_out(std::basic_string& str) { str.assign(start, finish); return true; } template bool stream_out(boost::container::basic_string& str) { str.assign(start, finish); return true; } template bool stream_out(std::array& output) noexcept { static_assert(sizeof(C) == sizeof(CharT), ""); return shr_std_array(output); } template bool stream_out(boost::array& output) noexcept { static_assert(sizeof(C) == sizeof(CharT), ""); return shr_std_array(output); } bool stream_out(bool& output) noexcept { output = false; // Suppress warning about uninitalized variable if (start == finish) return false; CharT const zero = lcast_char_constants::zero; CharT const plus = lcast_char_constants::plus; CharT const minus = lcast_char_constants::minus; const CharT* const dec_finish = finish - 1; output = Traits::eq(*dec_finish, zero + 1); if (!output && !Traits::eq(*dec_finish, zero)) { return false; // Does not ends on '0' or '1' } if (start == dec_finish) return true; // We may have sign at the beginning if (Traits::eq(plus, *start) || (Traits::eq(minus, *start) && !output)) { ++ start; } // Skipping zeros while (start != dec_finish) { if (!Traits::eq(zero, *start)) { return false; // Not a zero => error } ++ start; } return true; } private: // Not optimised converter template bool float_types_converter_internal(T& output) { if (parse_inf_nan(start, finish, output)) return true; bool const return_value = shr_using_base_class(output); /* Some compilers and libraries successfully * parse 'inf', 'INFINITY', '1.0E', '1.0E-'... * We are trying to provide a unified behaviour, * so we just forbid such conversions (as some * of the most popular compilers/libraries do) * */ CharT const minus = lcast_char_constants::minus; CharT const plus = lcast_char_constants::plus; CharT const capital_e = lcast_char_constants::capital_e; CharT const lowercase_e = lcast_char_constants::lowercase_e; if ( return_value && ( Traits::eq(*(finish-1), lowercase_e) // 1.0e || Traits::eq(*(finish-1), capital_e) // 1.0E || Traits::eq(*(finish-1), minus) // 1.0e- or 1.0E- || Traits::eq(*(finish-1), plus) // 1.0e+ or 1.0E+ ) ) return false; return return_value; } public: bool stream_out(float& output) { return float_types_converter_internal(output); } bool stream_out(double& output) { return float_types_converter_internal(output); } bool stream_out(long double& output) { return float_types_converter_internal(output); } // Generic istream-based algorithm. // lcast_streambuf_for_target::value is true. template bool stream_out(InputStreamable& output) { return shr_using_base_class(output); } }; }}} // namespace boost::detail::lcast #undef BOOST_LCAST_NO_WCHAR_T #endif // BOOST_LEXICAL_CAST_DETAIL_CONVERTER_LEXICAL_HPP