diff --git a/doc/quoted_manip.html b/doc/quoted_manip.html new file mode 100644 index 0000000..220c445 --- /dev/null +++ b/doc/quoted_manip.html @@ -0,0 +1,162 @@ + + + + + + Boost "quoted" I/O manipulator + + + + + + + + + + + + + +
boost.png (6897 bytes) +

"Quoted" + I/O Manipulators
+ for Strings

+
+ + + + + +
+

"Quoted" + I/O Manipulators + for Strings are not yet accepted into Boost as public components. Thus the + header file is currently located in <boost/io/detail/quoted_manip.hpp>, and + this documentation page is not linked to from official documentation.

+ +

Introduction

+

C++ Standard library stream I/O for strings that contain embedded spaces +can produce unexpected results. For example,

+
+
std::stringstream ss;
+std::string original = "fooled you";
+std::string round_trip;
+
+ss << original;
+ss >> round_trip;
+
+std::cout << original;   // outputs: fooled you
+std::cout << round_trip; // outputs: fooled
+
+assert(original == round_trip); // assert will fire
+
+

The Boost quoted stream I/O manipulator places delimiters, defaulted +to the double-quote ("), around strings on output, and strips off +the delimiters on input. This ensures strings with embedded spaces round-trip as +desired. For example,

+
+
std::stringstream ss;
+std::string original = "fooled you";
+std::string round_trip;
+
+ss << quoted(original);
+ss >> quoted(round_trip);
+
+std::cout << quoted(original); // outputs: "fooled you"
+std::cout << round_trip;       // outputs: fooled you
+
+assert(original == round_trip); // assert will not fire
+
+

If the string contains the delimiter character, on output that character will +be preceded by an escape character, as will the escape character itself:

+
+
std::cout << quoted("'Jack & Jill'", '&', '\'');  // outputs: '&'Jack && Jill&''
+
+

Header <boost/io/quoted_manip.hpp> synopsis

+
namespace boost
+{
+  namespace io
+  {
+    // manipulator for const std::basic_string&
+
+    template <class Char, class Traits, class Alloc>
+    unspecified-type1 quoted(const std::basic_string<Char, Traits, Alloc>& string, Char escape='\\', Char delim='\"');
+
+    // manipulator for const C-string*
+
+    template <class Char>
+    unspecified-type2 quoted(const Char* string, Char escape='\\', Char delim='\"');
+
+    // manipulator for non-const std::basic_string&
+
+    template <class Char, class Traits, class Alloc>
+    unspecified-type3 quoted(std::basic_string<Char, Traits, Alloc>& string, Char escape='\\', Char delim='\"');
+  }
+}
+

unspecified_type1, unspecified_type2, +and unspecified_type3 are implementation supplied +types with implementation supplied operator<<:

+
+
template <class Char, class Traits>
+  std::basic_ostream<Char, Traits>&
+    operator<<(std::basic_ostream<Char, Traits>& os, const unspecified_typeN& proxy);
+

Effects: Inserts characters into os:

+ +

Remarks: string, escape, and delim +have the type and value of the corresponding arguments of the call to the +quoted function that constructed proxy.

+

Returns: os.

+
+

unspecified_type3 is an implementation supplied +type with an implementation supplied operator>>:

+
+
template <class Char, class Traits>
+  std::basic_istream<Char, Traits>&
+    operator>>(std::basic_istream<Char, Traits>& is, const unspecified_type3& proxy);
+

Effects: Extracts characters from os:

+ +

Remarks: string, escape, and delim +have the type and value of the corresponding arguments of the call to the +quoted function that constructed proxy.

+

Returns: is.

+
+

Acknowledgements

+

The quoted() stream manipulator emerged from discussions on the +Boost developers mailing list. Participants included Beman Dawes, Rob Stewart, +Alexander Lamaison, Eric Niebler, Vicente Botet, Andrey Semashev, Phil Richards, +and Rob Murray. Eric Niebler's suggestions provided the basis for the name and +form of the templates.

+
+

© Copyright Beman Dawes, 2002, 2006, 2007, 2009, 2010

+

Distributed under the Boost Software License, Version 1.0. See +www.boost.org/LICENSE_1_0.txt

+

Revised +19 June 2010

+ + + \ No newline at end of file diff --git a/include/boost/io/detail/quoted_manip.hpp b/include/boost/io/detail/quoted_manip.hpp new file mode 100644 index 0000000..d915521 --- /dev/null +++ b/include/boost/io/detail/quoted_manip.hpp @@ -0,0 +1,187 @@ +// boost/io/quoted_manip.hpp ---------------------------------------------------------// + +// Copyright Beman Dawes 2010 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt + +// Library home page http://www.boost.org/libs/io + +//--------------------------------------------------------------------------------------// + +#ifndef BOOST_IO_QUOTED_MANIP +#define BOOST_IO_QUOTED_MANIP + +#include +#include +#include +#include +#include + +namespace boost +{ + namespace io + { + namespace detail { template struct quoted_proxy; } + + // ------------ public interface ------------------------------------------------// + + // manipulator for const std::basic_string& + template + detail::quoted_proxy const &, Char> + quoted(const std::basic_string& s, + Char escape='\\', Char delim='\"'); + + // manipulator for non-const std::basic_string& + template + detail::quoted_proxy &, Char> + quoted(std::basic_string& s, + Char escape='\\', Char delim='\"'); + + // manipulator for const C-string* + template + detail::quoted_proxy + quoted(const Char* s, Char escape='\\', Char delim='\"'); + + // ----------- implementation details -------------------------------------------// + + namespace detail + { + // proxy used as an argument pack + template + struct quoted_proxy + { + String string; + Char escape; + Char delim; + + quoted_proxy(String s_, Char escape_, Char delim_) + : string(s_), escape(escape_), delim(delim_) {} + }; + + // abstract away difference between proxies with const or non-const basic_strings + template + std::basic_ostream& + basic_string_inserter_imp(std::basic_ostream& os, + std::basic_string const & string, Char escape, Char delim) + { + os << delim; + typename std::basic_string::const_iterator + end_it = string.end(); + for (typename std::basic_string::const_iterator + it = string.begin(); + it != end_it; + ++it ) + { + if (*it == delim || *it == escape) + os << escape; + os << *it; + } + os << delim; + return os; + } + + // inserter for const std::basic_string& proxies + template + inline + std::basic_ostream& operator<<(std::basic_ostream& os, + const quoted_proxy const &, Char>& proxy) + { + return basic_string_inserter_imp(os, proxy.string, proxy.escape, proxy.delim); + } + + // inserter for non-const std::basic_string& proxies + template + inline + std::basic_ostream& operator<<(std::basic_ostream& os, + const quoted_proxy&, Char>& proxy) + { + return basic_string_inserter_imp(os, proxy.string, proxy.escape, proxy.delim); + } + + // inserter for const C-string* proxies + template + std::basic_ostream& operator<<(std::basic_ostream& os, + const quoted_proxy& proxy) + { + os << proxy.delim; + for (const Char* it = proxy.string; + *it; + ++it ) + { + if (*it == proxy.delim || *it == proxy.escape) + os << proxy.escape; + os << *it; + } + os << proxy.delim; + return os; + } + + // extractor for non-const std::basic_string& proxies + template + std::basic_istream& operator>>(std::basic_istream& is, + const quoted_proxy&, Char>& proxy) + { + Char c; + is >> c; + if (c != proxy.delim) + { + proxy.string = c; + is >> proxy.string; + return is; + } + proxy.string.clear(); + { + boost::io::ios_flags_saver ifs(is); + is >> std::noskipws; + for (;;) + { + is >> c; + if (!is.good()) // cope with I/O errors or end-of-file + break; + if (c == proxy.escape) + { + is >> c; + if (!is.good()) // cope with I/O errors or end-of-file + break; + } + else if (c == proxy.delim) + break; + proxy.string += c; + } + } + return is; + } + + } // namespace detail + + // manipulator implementation for const std::basic_string& + template + inline detail::quoted_proxy const &, Char> + quoted(const std::basic_string& s, Char escape, Char delim) + { + return detail::quoted_proxy const &, Char> + (s, escape, delim); + } + + // manipulator implementation for non-const std::basic_string& + template + inline detail::quoted_proxy &, Char> + quoted(std::basic_string& s, Char escape, Char delim) + { + return detail::quoted_proxy&, Char> + (s, escape, delim); + } + + // manipulator implementation for const C-string* + template + inline detail::quoted_proxy + quoted(const Char* s, Char escape, Char delim) + { + return detail::quoted_proxy (s, escape, delim); + } + + } // namespace io +} // namespace boost + +#endif // BOOST_IO_QUOTED_MANIP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 14b3e5b..4b48bce 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -21,4 +21,6 @@ test-suite "io" : # input files # : std::locale-support ] + + [ run quoted_manip_test.cpp ] ; diff --git a/test/quoted_manip_test.cpp b/test/quoted_manip_test.cpp new file mode 100644 index 0000000..eb481b7 --- /dev/null +++ b/test/quoted_manip_test.cpp @@ -0,0 +1,92 @@ +// libs/io/test/quote_manip_test.cpp ----------------------------------------------- // + +// Copyright Beman Dawes 2010 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt + +// Library home page: http://www.boost.org/libs/io + +// ---------------------------------------------------------------------------------- // + +#include +#include +#include +#include + +using boost::io::quoted; +using std::string; +using std::wstring; + +int main() +{ + + std::stringstream ss; + std::wstringstream wss; + + const string s1("foo\\bar, \" *"); + string r; // test results + + ss << quoted(s1); + ss >> r; + BOOST_TEST_EQ(r, "\"foo\\\\bar, \\\" *\""); + + ss << quoted(s1.c_str()); + ss >> r; + BOOST_TEST_EQ(r, "\"foo\\\\bar, \\\" *\""); + + ss << quoted(s1); + ss >> quoted(r); + BOOST_TEST_EQ(r, s1); + + ss << quoted(s1.c_str()); + ss >> quoted(r); + BOOST_TEST_EQ(r, s1); + + string s2("'Jack & Jill'"); + + ss << quoted(s2, '&', '\''); + ss >> r; + BOOST_TEST_EQ(r, "'&'Jack && Jill&''"); + + ss << quoted(s2, '&', '\''); + ss >> quoted(r, '&', '\''); + BOOST_TEST_EQ(r, s2); + + wstring ws1(L"foo$bar, \" *"); + wstring wr; // test results + + wss << quoted(ws1, L'$'); + wss >> wr; + BOOST_TEST(wr == wstring(L"\"foo$$bar, $\" *\"")); + + wss << quoted(ws1, L'$'); + wss >> quoted(wr, L'$'); + BOOST_TEST(wr == ws1); + + const string s3("const string"); + ss << quoted(s3); + ss >> quoted(r); + BOOST_TEST_EQ(r, s3); + + // missing end delimiter test + ss << "\"abc"; // load ss with faulty quoting + ss >> quoted(r); // this loops if istream error/eof not detected + BOOST_TEST_EQ(r, "abc"); + + // no initial delmiter test + ss << "abc"; + ss >> quoted(r); + BOOST_TEST_EQ(r, "abc"); + + // no initial delmiter, space in ss + ss << "abc def"; + ss >> quoted(r); + BOOST_TEST_EQ(r, "abc"); + + // these should fail to compile because the arguments are const: + // ss >> quoted(s1); + // ss >> quoted("foo"); + + return 0; +}