stream.hpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Copyright (c) 2009-2020 Vladimir Batov.
  2. // Use, modification and distribution are subject to the Boost Software License,
  3. // Version 1.0. See http://www.boost.org/LICENSE_1_0.txt.
  4. #ifndef BOOST_CONVERT_STRINGSTREAM_BASED_CONVERTER_HPP
  5. #define BOOST_CONVERT_STRINGSTREAM_BASED_CONVERTER_HPP
  6. #include <boost/convert/parameters.hpp>
  7. #include <boost/convert/detail/is_string.hpp>
  8. #include <boost/make_default.hpp>
  9. #include <sstream>
  10. #include <iomanip>
  11. #define BOOST_CNV_STRING_ENABLE \
  12. template<typename string_type, typename type> \
  13. typename std::enable_if<cnv::is_string<string_type>::value, void>::type \
  14. operator()
  15. #define BOOST_CNV_PARAM_SET(param_name) \
  16. template <typename argument_pack> \
  17. void set_( \
  18. argument_pack const& arg, \
  19. cnv::parameter::type::param_name, \
  20. mpl::true_)
  21. #define BOOST_CNV_PARAM_TRY(param_name) \
  22. this->set_( \
  23. arg, \
  24. cnv::parameter::type::param_name(), \
  25. typename mpl::has_key< \
  26. argument_pack, cnv::parameter::type::param_name>::type());
  27. namespace boost { namespace cnv
  28. {
  29. template<class Char> struct basic_stream;
  30. using cstream = boost::cnv::basic_stream<char>;
  31. using wstream = boost::cnv::basic_stream<wchar_t>;
  32. }}
  33. template<class Char>
  34. struct boost::cnv::basic_stream
  35. {
  36. // C01. In string-to-type conversions the "string" must be a CONTIGUOUS ARRAY of
  37. // characters because "ibuffer_type" uses/relies on that (it deals with char_type*).
  38. // C02. Use the provided "string_in" as the input (read-from) buffer and, consequently,
  39. // avoid the overhead associated with stream_.str(string_in) --
  40. // copying of the content into internal buffer.
  41. // C03. The "strbuf.gptr() != strbuf.egptr()" check replaces "istream.eof() != true"
  42. // which for some reason does not work when we try converting the "true" string
  43. // to "bool" with std::boolalpha set. Seems that istream state gets unsynced compared
  44. // to the actual underlying buffer.
  45. using char_type = Char;
  46. using this_type = boost::cnv::basic_stream<char_type>;
  47. using stream_type = std::basic_stringstream<char_type>;
  48. using istream_type = std::basic_istream<char_type>;
  49. using buffer_type = std::basic_streambuf<char_type>;
  50. using stdstr_type = std::basic_string<char_type>;
  51. using manipulator_type = std::ios_base& (*)(std::ios_base&);
  52. struct ibuffer_type : buffer_type
  53. {
  54. using buffer_type::eback;
  55. using buffer_type::gptr;
  56. using buffer_type::egptr;
  57. ibuffer_type(char_type const* beg, std::size_t sz) //C01
  58. {
  59. char_type* b = const_cast<char_type*>(beg);
  60. buffer_type::setg(b, b, b + sz);
  61. }
  62. };
  63. struct obuffer_type : buffer_type
  64. {
  65. using buffer_type::pbase;
  66. using buffer_type::pptr;
  67. using buffer_type::epptr;
  68. };
  69. basic_stream () : stream_(std::ios_base::in | std::ios_base::out) {}
  70. basic_stream (this_type&& other) : stream_(std::move(other.stream_)) {}
  71. basic_stream(this_type const&) = delete;
  72. this_type& operator=(this_type const&) = delete;
  73. BOOST_CNV_STRING_ENABLE(type const& v, optional<string_type>& s) const { to_str(v, s); }
  74. BOOST_CNV_STRING_ENABLE(string_type const& s, optional<type>& r) const { str_to(cnv::range<string_type const>(s), r); }
  75. // Resolve ambiguity of string-to-string
  76. template<typename type> void operator()( char_type const* s, optional<type>& r) const { str_to(cnv::range< char_type const*>(s), r); }
  77. template<typename type> void operator()(stdstr_type const& s, optional<type>& r) const { str_to(cnv::range<stdstr_type const>(s), r); }
  78. // Formatters
  79. template<typename manipulator>
  80. typename boost::disable_if<boost::parameter::is_argument_pack<manipulator>, this_type&>::type
  81. operator()(manipulator m) { return (this->stream_ << m, *this); }
  82. this_type& operator() (manipulator_type m) { return (m(stream_), *this); }
  83. this_type& operator() (std::locale const& l) { return (stream_.imbue(l), *this); }
  84. template<typename argument_pack>
  85. typename std::enable_if<boost::parameter::is_argument_pack<argument_pack>::value, this_type&>::type
  86. operator()(argument_pack const& arg)
  87. {
  88. BOOST_CNV_PARAM_TRY(precision);
  89. BOOST_CNV_PARAM_TRY(width);
  90. BOOST_CNV_PARAM_TRY(fill);
  91. BOOST_CNV_PARAM_TRY(uppercase);
  92. BOOST_CNV_PARAM_TRY(skipws);
  93. BOOST_CNV_PARAM_TRY(adjust);
  94. BOOST_CNV_PARAM_TRY(base);
  95. BOOST_CNV_PARAM_TRY(notation);
  96. return *this;
  97. }
  98. private:
  99. template<typename argument_pack, typename keyword_tag>
  100. void set_(argument_pack const&, keyword_tag, mpl::false_) {}
  101. BOOST_CNV_PARAM_SET (locale) { stream_.imbue(arg[cnv::parameter::locale]); }
  102. BOOST_CNV_PARAM_SET (precision) { stream_.precision(arg[cnv::parameter::precision]); }
  103. BOOST_CNV_PARAM_SET (width) { stream_.width(arg[cnv::parameter::width]); }
  104. BOOST_CNV_PARAM_SET (fill) { stream_.fill(arg[cnv::parameter::fill]); }
  105. BOOST_CNV_PARAM_SET (uppercase)
  106. {
  107. bool uppercase = arg[cnv::parameter::uppercase];
  108. uppercase ? (void) stream_.setf(std::ios::uppercase) : stream_.unsetf(std::ios::uppercase);
  109. }
  110. BOOST_CNV_PARAM_SET (skipws)
  111. {
  112. bool skipws = arg[cnv::parameter::skipws];
  113. skipws ? (void) stream_.setf(std::ios::skipws) : stream_.unsetf(std::ios::skipws);
  114. }
  115. BOOST_CNV_PARAM_SET (adjust)
  116. {
  117. cnv::adjust adjust = arg[cnv::parameter::adjust];
  118. /**/ if (adjust == cnv::adjust:: left) stream_.setf(std::ios::adjustfield, std::ios:: left);
  119. else if (adjust == cnv::adjust::right) stream_.setf(std::ios::adjustfield, std::ios::right);
  120. else BOOST_ASSERT(!"Not implemented");
  121. }
  122. BOOST_CNV_PARAM_SET (base)
  123. {
  124. cnv::base base = arg[cnv::parameter::base];
  125. /**/ if (base == cnv::base::dec) std::dec(stream_);
  126. else if (base == cnv::base::hex) std::hex(stream_);
  127. else if (base == cnv::base::oct) std::oct(stream_);
  128. else BOOST_ASSERT(!"Not implemented");
  129. }
  130. BOOST_CNV_PARAM_SET (notation)
  131. {
  132. cnv::notation notation = arg[cnv::parameter::notation];
  133. /**/ if (notation == cnv::notation:: fixed) std::fixed(stream_);
  134. else if (notation == cnv::notation::scientific) std::scientific(stream_);
  135. else if (notation == cnv::notation:: hex) std::hexfloat(stream_);
  136. else BOOST_ASSERT(!"Not implemented");
  137. }
  138. template<typename string_type, typename out_type> void str_to(cnv::range<string_type>, optional<out_type>&) const;
  139. template<typename string_type, typename in_type> void to_str(in_type const&, optional<string_type>&) const;
  140. mutable stream_type stream_;
  141. };
  142. template<typename char_type>
  143. template<typename string_type, typename in_type>
  144. inline
  145. void
  146. boost::cnv::basic_stream<char_type>::to_str(
  147. in_type const& value_in,
  148. boost::optional<string_type>& string_out) const
  149. {
  150. stream_.clear(); // Clear the flags
  151. stream_.str(stdstr_type()); // Clear/empty the content of the stream
  152. if (!(stream_ << value_in).fail())
  153. {
  154. buffer_type* buf = stream_.rdbuf();
  155. obuffer_type* obuf = reinterpret_cast<obuffer_type*>(buf);
  156. char_type const* beg = obuf->pbase();
  157. char_type const* end = obuf->pptr();
  158. string_out = string_type(beg, end); // Instead of stream_.str();
  159. }
  160. }
  161. template<typename char_type>
  162. template<typename string_type, typename out_type>
  163. inline
  164. void
  165. boost::cnv::basic_stream<char_type>::str_to(
  166. boost::cnv::range<string_type> string_in,
  167. boost::optional<out_type>& result_out) const
  168. {
  169. if (string_in.empty ()) return;
  170. istream_type& istream = stream_;
  171. buffer_type* oldbuf = istream.rdbuf();
  172. char_type const* beg = &*string_in.begin();
  173. std::size_t sz = string_in.end() - string_in.begin();
  174. ibuffer_type newbuf (beg, sz); //C02
  175. istream.rdbuf(&newbuf);
  176. istream.clear(); // Clear the flags
  177. istream >> *(result_out = boost::make_default<out_type>());
  178. if (istream.fail() || newbuf.gptr() != newbuf.egptr()/*C03*/)
  179. result_out = boost::none;
  180. istream.rdbuf(oldbuf);
  181. }
  182. #undef BOOST_CNV_STRING_ENABLE
  183. #undef BOOST_CNV_PARAM_SET
  184. #undef BOOST_CNV_PARAM_TRY
  185. #endif // BOOST_CONVERT_STRINGSTREAM_BASED_CONVERTER_HPP