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