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>
11 #include <boost/noncopyable.hpp>
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&                                  \
22     operator()(boost::parameter::aux::tag<boost::cnv::parameter::type::PARAM_NAME, PARAM_TYPE const>::type const& arg)
23 
24 namespace boost { namespace cnv
25 {
26     template<class Char> struct basic_stream;
27 
28     using cstream = boost::cnv::basic_stream<char>;
29     using wstream = boost::cnv::basic_stream<wchar_t>;
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 
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 
53     struct ibuffer_type : buffer_type
54     {
55         using buffer_type::eback;
56         using buffer_type::gptr;
57         using buffer_type::egptr;
58 
ibuffer_typeboost::cnv::basic_stream::ibuffer_type59         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     };
66     struct obuffer_type : buffer_type
67     {
68         using buffer_type::pbase;
69         using buffer_type::pptr;
70         using buffer_type::epptr;
71     };
72 
basic_streamboost::cnv::basic_stream73     basic_stream() : stream_(std::ios_base::in | std::ios_base::out) {}
74 #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )
basic_streamboost::cnv::basic_stream75     basic_stream(this_type&& other) : stream_(std::move(other.stream_)) {}
76 #endif
77 
BOOST_CNV_STRING_ENABLEboost::cnv::basic_stream78     BOOST_CNV_STRING_ENABLE(type const& v, optional<string_type>& s) const { to_str(v, s); }
BOOST_CNV_STRING_ENABLEboost::cnv::basic_stream79     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
operator ()boost::cnv::basic_stream81     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_stream82     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>
operator ()boost::cnv::basic_stream86     this_type& operator() (manipulator m) { return (stream_ << m, *this); }
operator ()boost::cnv::basic_stream87     this_type& operator() (manipulator_type m) { return (m(stream_), *this); }
operator ()boost::cnv::basic_stream88     this_type& operator() (std::locale const& l) { return (stream_.imbue(l), *this); }
89 
BOOST_CNV_PARAMboost::cnv::basic_stream90     BOOST_CNV_PARAM(locale, std::locale) { return (stream_.imbue(arg[cnv::parameter::locale]), *this); }
BOOST_CNV_PARAMboost::cnv::basic_stream91     BOOST_CNV_PARAM(precision,      int) { return (stream_.precision(arg[cnv::parameter::precision]), *this); }
BOOST_CNV_PARAMboost::cnv::basic_stream92     BOOST_CNV_PARAM(width,          int) { return (stream_.width(arg[cnv::parameter::width]), *this); }
BOOST_CNV_PARAMboost::cnv::basic_stream93     BOOST_CNV_PARAM(fill,          char) { return (stream_.fill(arg[cnv::parameter::fill]), *this); }
BOOST_CNV_PARAMboost::cnv::basic_stream94     BOOST_CNV_PARAM(uppercase,     bool)
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     }
BOOST_CNV_PARAMboost::cnv::basic_stream100     BOOST_CNV_PARAM(skipws, bool)
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     }
BOOST_CNV_PARAMboost::cnv::basic_stream106     BOOST_CNV_PARAM(adjust, boost::cnv::adjust)
107     {
108         cnv::adjust adjust = arg[cnv::parameter::adjust];
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     }
BOOST_CNV_PARAMboost::cnv::basic_stream116     BOOST_CNV_PARAM(base, boost::cnv::base)
117     {
118         cnv::base base = arg[cnv::parameter::base];
119 
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");
124 
125         return *this;
126     }
BOOST_CNV_PARAMboost::cnv::basic_stream127     BOOST_CNV_PARAM(notation, boost::cnv::notation)
128     {
129         cnv::notation notation = arg[cnv::parameter::notation];
130 
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");
134 
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
to_str(in_type const & value_in,boost::optional<string_type> & string_out) const150 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();
160         obuffer_type*   obuf = reinterpret_cast<obuffer_type*>(buf);
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
str_to(boost::cnv::range<string_type> string_in,boost::optional<out_type> & result_out) const172 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
199