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 
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>
11 #include <sstream>
12 #include <iomanip>
13 
14 #define BOOST_CNV_STRING_ENABLE                                             \
15     template<typename string_type, typename type>                           \
16     typename std::enable_if<cnv::is_string<string_type>::value, void>::type \
17     operator()
18 
19 #define BOOST_CNV_PARAM_SET(param_name)   \
20     template <typename argument_pack>     \
21     void set_(                            \
22         argument_pack const& arg,         \
23         cnv::parameter::type::param_name, \
24         mpl::true_)
25 
26 #define BOOST_CNV_PARAM_TRY(param_name)     \
27     this->set_(                             \
28         arg,                                \
29         cnv::parameter::type::param_name(), \
30         typename mpl::has_key<              \
31             argument_pack, cnv::parameter::type::param_name>::type());
32 
33 namespace boost { namespace cnv
34 {
35     template<class Char> struct basic_stream;
36 
37     using cstream = boost::cnv::basic_stream<char>;
38     using wstream = boost::cnv::basic_stream<wchar_t>;
39 }}
40 
41 template<class Char>
42 struct boost::cnv::basic_stream
43 {
44     // C01. In string-to-type conversions the "string" must be a CONTIGUOUS ARRAY of
45     //      characters because "ibuffer_type" uses/relies on that (it deals with char_type*).
46     // C02. Use the provided "string_in" as the input (read-from) buffer and, consequently,
47     //      avoid the overhead associated with stream_.str(string_in) --
48     //      copying of the content into internal buffer.
49     // C03. The "strbuf.gptr() != strbuf.egptr()" check replaces "istream.eof() != true"
50     //      which for some reason does not work when we try converting the "true" string
51     //      to "bool" with std::boolalpha set. Seems that istream state gets unsynced compared
52     //      to the actual underlying buffer.
53 
54     using        char_type = Char;
55     using        this_type = boost::cnv::basic_stream<char_type>;
56     using      stream_type = std::basic_stringstream<char_type>;
57     using     istream_type = std::basic_istream<char_type>;
58     using      buffer_type = std::basic_streambuf<char_type>;
59     using      stdstr_type = std::basic_string<char_type>;
60     using manipulator_type = std::ios_base& (*)(std::ios_base&);
61 
62     struct ibuffer_type : buffer_type
63     {
64         using buffer_type::eback;
65         using buffer_type::gptr;
66         using buffer_type::egptr;
67 
ibuffer_typeboost::cnv::basic_stream::ibuffer_type68         ibuffer_type(char_type const* beg, std::size_t sz) //C01
69         {
70             char_type* b = const_cast<char_type*>(beg);
71 
72             buffer_type::setg(b, b, b + sz);
73         }
74     };
75     struct obuffer_type : buffer_type
76     {
77         using buffer_type::pbase;
78         using buffer_type::pptr;
79         using buffer_type::epptr;
80     };
81 
basic_streamboost::cnv::basic_stream82     basic_stream () : stream_(std::ios_base::in | std::ios_base::out) {}
basic_streamboost::cnv::basic_stream83     basic_stream (this_type&& other) : stream_(std::move(other.stream_)) {}
84 
85     basic_stream(this_type const&) = delete;
86     this_type& operator=(this_type const&) = delete;
87 
BOOST_CNV_STRING_ENABLEboost::cnv::basic_stream88     BOOST_CNV_STRING_ENABLE(type const& v, optional<string_type>& s) const { to_str(v, s); }
BOOST_CNV_STRING_ENABLEboost::cnv::basic_stream89     BOOST_CNV_STRING_ENABLE(string_type const& s, optional<type>& r) const { str_to(cnv::range<string_type const>(s), r); }
90 
91     // Resolve ambiguity of string-to-string
operator ()boost::cnv::basic_stream92     template<typename type> void operator()(  char_type const* s, optional<type>& r) const { str_to(cnv::range< char_type const*>(s), r); }
operator ()boost::cnv::basic_stream93     template<typename type> void operator()(stdstr_type const& s, optional<type>& r) const { str_to(cnv::range<stdstr_type const>(s), r); }
94 
95     // Formatters
96     template<typename manipulator>
97     typename boost::disable_if<boost::parameter::is_argument_pack<manipulator>, this_type&>::type
operator ()boost::cnv::basic_stream98     operator()(manipulator m) { return (this->stream_ << m, *this); }
99 
operator ()boost::cnv::basic_stream100     this_type& operator() (manipulator_type m) { return (m(stream_), *this); }
operator ()boost::cnv::basic_stream101     this_type& operator() (std::locale const& l) { return (stream_.imbue(l), *this); }
102 
103     template<typename argument_pack>
104     typename std::enable_if<boost::parameter::is_argument_pack<argument_pack>::value, this_type&>::type
operator ()boost::cnv::basic_stream105     operator()(argument_pack const& arg)
106     {
107         BOOST_CNV_PARAM_TRY(precision);
108         BOOST_CNV_PARAM_TRY(width);
109         BOOST_CNV_PARAM_TRY(fill);
110         BOOST_CNV_PARAM_TRY(uppercase);
111         BOOST_CNV_PARAM_TRY(skipws);
112         BOOST_CNV_PARAM_TRY(adjust);
113         BOOST_CNV_PARAM_TRY(base);
114         BOOST_CNV_PARAM_TRY(notation);
115 
116         return *this;
117     }
118 
119     private:
120 
121     template<typename argument_pack, typename keyword_tag>
set_boost::cnv::basic_stream122     void set_(argument_pack const&, keyword_tag, mpl::false_) {}
123 
BOOST_CNV_PARAM_SETboost::cnv::basic_stream124     BOOST_CNV_PARAM_SET (locale)    { stream_.imbue(arg[cnv::parameter::locale]); }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream125     BOOST_CNV_PARAM_SET (precision) { stream_.precision(arg[cnv::parameter::precision]); }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream126     BOOST_CNV_PARAM_SET (width)     { stream_.width(arg[cnv::parameter::width]); }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream127     BOOST_CNV_PARAM_SET (fill)      { stream_.fill(arg[cnv::parameter::fill]); }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream128     BOOST_CNV_PARAM_SET (uppercase)
129     {
130         bool uppercase = arg[cnv::parameter::uppercase];
131         uppercase ? (void) stream_.setf(std::ios::uppercase) : stream_.unsetf(std::ios::uppercase);
132     }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream133     BOOST_CNV_PARAM_SET (skipws)
134     {
135         bool skipws = arg[cnv::parameter::skipws];
136         skipws ? (void) stream_.setf(std::ios::skipws) : stream_.unsetf(std::ios::skipws);
137     }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream138     BOOST_CNV_PARAM_SET (adjust)
139     {
140         cnv::adjust adjust = arg[cnv::parameter::adjust];
141 
142         /**/ if (adjust == cnv::adjust:: left) stream_.setf(std::ios::adjustfield, std::ios:: left);
143         else if (adjust == cnv::adjust::right) stream_.setf(std::ios::adjustfield, std::ios::right);
144         else BOOST_ASSERT(!"Not implemented");
145     }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream146     BOOST_CNV_PARAM_SET (base)
147     {
148         cnv::base base = arg[cnv::parameter::base];
149 
150         /**/ if (base == cnv::base::dec) std::dec(stream_);
151         else if (base == cnv::base::hex) std::hex(stream_);
152         else if (base == cnv::base::oct) std::oct(stream_);
153         else BOOST_ASSERT(!"Not implemented");
154     }
BOOST_CNV_PARAM_SETboost::cnv::basic_stream155     BOOST_CNV_PARAM_SET (notation)
156     {
157         cnv::notation notation = arg[cnv::parameter::notation];
158 
159         /**/ if (notation == cnv::notation::     fixed)      std::fixed(stream_);
160         else if (notation == cnv::notation::scientific) std::scientific(stream_);
161         else BOOST_ASSERT(!"Not implemented");
162     }
163 
164     template<typename string_type, typename out_type> void str_to(cnv::range<string_type>, optional<out_type>&) const;
165     template<typename string_type, typename  in_type> void to_str(in_type const&, optional<string_type>&) const;
166 
167     mutable stream_type stream_;
168 };
169 
170 template<typename char_type>
171 template<typename string_type, typename in_type>
172 inline
173 void
to_str(in_type const & value_in,boost::optional<string_type> & string_out) const174 boost::cnv::basic_stream<char_type>::to_str(
175     in_type const& value_in,
176     boost::optional<string_type>& string_out) const
177 {
178     stream_.clear();            // Clear the flags
179     stream_.str(stdstr_type()); // Clear/empty the content of the stream
180 
181     if (!(stream_ << value_in).fail())
182     {
183         buffer_type*     buf = stream_.rdbuf();
184         obuffer_type*   obuf = reinterpret_cast<obuffer_type*>(buf);
185         char_type const* beg = obuf->pbase();
186         char_type const* end = obuf->pptr();
187 
188         string_out = string_type(beg, end); // Instead of stream_.str();
189     }
190 }
191 
192 template<typename char_type>
193 template<typename string_type, typename out_type>
194 inline
195 void
str_to(boost::cnv::range<string_type> string_in,boost::optional<out_type> & result_out) const196 boost::cnv::basic_stream<char_type>::str_to(
197     boost::cnv::range<string_type> string_in,
198     boost::optional<out_type>& result_out) const
199 {
200     if (string_in.empty ()) return;
201 
202     istream_type& istream = stream_;
203     buffer_type*   oldbuf = istream.rdbuf();
204     char_type const*  beg = &*string_in.begin();
205     std::size_t        sz = string_in.end() - string_in.begin();
206     ibuffer_type   newbuf (beg, sz); //C02
207 
208     istream.rdbuf(&newbuf);
209     istream.clear(); // Clear the flags
210 
211     istream >> *(result_out = boost::make_default<out_type>());
212 
213     if (istream.fail() || newbuf.gptr() != newbuf.egptr()/*C03*/)
214         result_out = boost::none;
215 
216     istream.rdbuf(oldbuf);
217 }
218 
219 #undef BOOST_CNV_STRING_ENABLE
220 #undef BOOST_CNV_PARAM_SET
221 #undef BOOST_CNV_PARAM_TRY
222 
223 #endif // BOOST_CONVERT_STRINGSTREAM_BASED_CONVERTER_HPP
224