You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
7.5 KiB
198 lines
7.5 KiB
// Copyright (c) 2009-2016 Vladimir Batov. |
|
// Use, modification and distribution are subject to the Boost Software License, |
|
// Version 1.0. See http://www.boost.org/LICENSE_1_0.txt. |
|
|
|
#ifndef BOOST_CONVERT_STRINGSTREAM_BASED_CONVERTER_HPP |
|
#define BOOST_CONVERT_STRINGSTREAM_BASED_CONVERTER_HPP |
|
|
|
#include <boost/convert/parameters.hpp> |
|
#include <boost/convert/detail/is_string.hpp> |
|
#include <boost/make_default.hpp> |
|
#include <boost/noncopyable.hpp> |
|
#include <sstream> |
|
#include <iomanip> |
|
|
|
#define BOOST_CNV_STRING_ENABLE \ |
|
template<typename string_type, typename type> \ |
|
typename boost::enable_if<cnv::is_string<string_type>, void>::type \ |
|
operator() |
|
|
|
#define BOOST_CNV_PARAM(PARAM_NAME, PARAM_TYPE) \ |
|
this_type& \ |
|
operator()(boost::parameter::aux::tag<boost::cnv::parameter::type::PARAM_NAME, PARAM_TYPE const>::type const& arg) |
|
|
|
namespace boost { namespace cnv |
|
{ |
|
template<class Char> struct basic_stream; |
|
|
|
using cstream = boost::cnv::basic_stream<char>; |
|
using wstream = boost::cnv::basic_stream<wchar_t>; |
|
}} |
|
|
|
template<class Char> |
|
struct boost::cnv::basic_stream : boost::noncopyable |
|
{ |
|
// C01. In string-to-type conversions the "string" must be a CONTIGUOUS ARRAY of |
|
// characters because "ibuffer_type" uses/relies on that (it deals with char_type*). |
|
// C02. Use the provided "string_in" as the input (read-from) buffer and, consequently, |
|
// avoid the overhead associated with stream_.str(string_in) -- |
|
// copying of the content into internal buffer. |
|
// C03. The "strbuf.gptr() != strbuf.egptr()" check replaces "istream.eof() != true" |
|
// which for some reason does not work when we try converting the "true" string |
|
// to "bool" with std::boolalpha set. Seems that istream state gets unsynced compared |
|
// to the actual underlying buffer. |
|
|
|
using char_type = Char; |
|
using this_type = boost::cnv::basic_stream<char_type>; |
|
using stream_type = std::basic_stringstream<char_type>; |
|
using istream_type = std::basic_istream<char_type>; |
|
using buffer_type = std::basic_streambuf<char_type>; |
|
using stdstr_type = std::basic_string<char_type>; |
|
using manipulator_type = std::ios_base& (*)(std::ios_base&); |
|
|
|
struct ibuffer_type : buffer_type |
|
{ |
|
using buffer_type::eback; |
|
using buffer_type::gptr; |
|
using buffer_type::egptr; |
|
|
|
ibuffer_type(char_type const* beg, std::size_t sz) //C01 |
|
{ |
|
char_type* b = const_cast<char_type*>(beg); |
|
|
|
buffer_type::setg(b, b, b + sz); |
|
} |
|
}; |
|
struct obuffer_type : buffer_type |
|
{ |
|
using buffer_type::pbase; |
|
using buffer_type::pptr; |
|
using buffer_type::epptr; |
|
}; |
|
|
|
basic_stream() : stream_(std::ios_base::in | std::ios_base::out) {} |
|
#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) |
|
basic_stream(this_type&& other) : stream_(std::move(other.stream_)) {} |
|
#endif |
|
|
|
BOOST_CNV_STRING_ENABLE(type const& v, optional<string_type>& s) const { to_str(v, s); } |
|
BOOST_CNV_STRING_ENABLE(string_type const& s, optional<type>& r) const { str_to(cnv::range<string_type const>(s), r); } |
|
// Resolve ambiguity of string-to-string |
|
template<typename type> void operator()( char_type const* s, optional<type>& r) const { str_to(cnv::range< char_type const*>(s), r); } |
|
template<typename type> void operator()(stdstr_type const& s, optional<type>& r) const { str_to(cnv::range<stdstr_type const>(s), r); } |
|
|
|
// Formatters |
|
template<typename manipulator> |
|
this_type& operator() (manipulator m) { return (stream_ << m, *this); } |
|
this_type& operator() (manipulator_type m) { return (m(stream_), *this); } |
|
this_type& operator() (std::locale const& l) { return (stream_.imbue(l), *this); } |
|
|
|
BOOST_CNV_PARAM(locale, std::locale) { return (stream_.imbue(arg[cnv::parameter::locale]), *this); } |
|
BOOST_CNV_PARAM(precision, int) { return (stream_.precision(arg[cnv::parameter::precision]), *this); } |
|
BOOST_CNV_PARAM(width, int) { return (stream_.width(arg[cnv::parameter::width]), *this); } |
|
BOOST_CNV_PARAM(fill, char) { return (stream_.fill(arg[cnv::parameter::fill]), *this); } |
|
BOOST_CNV_PARAM(uppercase, bool) |
|
{ |
|
bool uppercase = arg[cnv::parameter::uppercase]; |
|
uppercase ? (void) stream_.setf(std::ios::uppercase) : stream_.unsetf(std::ios::uppercase); |
|
return *this; |
|
} |
|
BOOST_CNV_PARAM(skipws, bool) |
|
{ |
|
bool skipws = arg[cnv::parameter::skipws]; |
|
skipws ? (void) stream_.setf(std::ios::skipws) : stream_.unsetf(std::ios::skipws); |
|
return *this; |
|
} |
|
BOOST_CNV_PARAM(adjust, boost::cnv::adjust) |
|
{ |
|
cnv::adjust adjust = arg[cnv::parameter::adjust]; |
|
|
|
/**/ if (adjust == cnv::adjust:: left) stream_.setf(std::ios::adjustfield, std::ios:: left); |
|
else if (adjust == cnv::adjust::right) stream_.setf(std::ios::adjustfield, std::ios::right); |
|
else BOOST_ASSERT(!"Not implemented"); |
|
|
|
return *this; |
|
} |
|
BOOST_CNV_PARAM(base, boost::cnv::base) |
|
{ |
|
cnv::base base = arg[cnv::parameter::base]; |
|
|
|
/**/ if (base == cnv::base::dec) std::dec(stream_); |
|
else if (base == cnv::base::hex) std::hex(stream_); |
|
else if (base == cnv::base::oct) std::oct(stream_); |
|
else BOOST_ASSERT(!"Not implemented"); |
|
|
|
return *this; |
|
} |
|
BOOST_CNV_PARAM(notation, boost::cnv::notation) |
|
{ |
|
cnv::notation notation = arg[cnv::parameter::notation]; |
|
|
|
/**/ if (notation == cnv::notation:: fixed) std::fixed(stream_); |
|
else if (notation == cnv::notation::scientific) std::scientific(stream_); |
|
else BOOST_ASSERT(!"Not implemented"); |
|
|
|
return *this; |
|
} |
|
|
|
private: |
|
|
|
template<typename string_type, typename out_type> void str_to(cnv::range<string_type>, optional<out_type>&) const; |
|
template<typename string_type, typename in_type> void to_str(in_type const&, optional<string_type>&) const; |
|
|
|
mutable stream_type stream_; |
|
}; |
|
|
|
template<typename char_type> |
|
template<typename string_type, typename in_type> |
|
inline |
|
void |
|
boost::cnv::basic_stream<char_type>::to_str( |
|
in_type const& value_in, |
|
boost::optional<string_type>& string_out) const |
|
{ |
|
stream_.clear(); // Clear the flags |
|
stream_.str(stdstr_type()); // Clear/empty the content of the stream |
|
|
|
if (!(stream_ << value_in).fail()) |
|
{ |
|
buffer_type* buf = stream_.rdbuf(); |
|
obuffer_type* obuf = reinterpret_cast<obuffer_type*>(buf); |
|
char_type const* beg = obuf->pbase(); |
|
char_type const* end = obuf->pptr(); |
|
|
|
string_out = string_type(beg, end); // Instead of stream_.str(); |
|
} |
|
} |
|
|
|
template<typename char_type> |
|
template<typename string_type, typename out_type> |
|
inline |
|
void |
|
boost::cnv::basic_stream<char_type>::str_to( |
|
boost::cnv::range<string_type> string_in, |
|
boost::optional<out_type>& result_out) const |
|
{ |
|
if (string_in.empty ()) return; |
|
|
|
istream_type& istream = stream_; |
|
buffer_type* oldbuf = istream.rdbuf(); |
|
char_type const* beg = &*string_in.begin(); |
|
std::size_t sz = string_in.end() - string_in.begin(); |
|
ibuffer_type newbuf (beg, sz); //C02 |
|
|
|
istream.rdbuf(&newbuf); |
|
istream.clear(); // Clear the flags |
|
|
|
istream >> *(result_out = boost::make_default<out_type>()); |
|
|
|
if (istream.fail() || newbuf.gptr() != newbuf.egptr()/*C03*/) |
|
result_out = boost::none; |
|
|
|
istream.rdbuf(oldbuf); |
|
} |
|
|
|
#undef BOOST_CNV_STRING_ENABLE |
|
#undef BOOST_CNV_PARAM |
|
|
|
#endif // BOOST_CONVERT_STRINGSTREAM_BASED_CONVERTER_HPP
|
|
|