From 2dc89e1eeefa7b55c284f34ab512d19b087e6d53 Mon Sep 17 00:00:00 2001 From: Glen Fernandes Date: Sat, 14 Dec 2019 07:56:15 -0500 Subject: [PATCH 1/5] Implement ostream_joiner --- doc/io-docinfo-footer.html | 5 + doc/io.adoc | 4 +- doc/ostream_joiner.adoc | 143 ++++++++++++++++++++++++++++ include/boost/io/ostream_joiner.hpp | 118 +++++++++++++++++++++++ meta/libraries.json | 5 +- test/Jamfile.v2 | 2 + test/make_ostream_joiner_test.cpp | 21 ++++ test/ostream_joiner_test.cpp | 116 ++++++++++++++++++++++ 8 files changed, 411 insertions(+), 3 deletions(-) create mode 100644 doc/io-docinfo-footer.html create mode 100644 doc/ostream_joiner.adoc create mode 100644 include/boost/io/ostream_joiner.hpp create mode 100644 test/make_ostream_joiner_test.cpp create mode 100644 test/ostream_joiner_test.cpp diff --git a/doc/io-docinfo-footer.html b/doc/io-docinfo-footer.html new file mode 100644 index 0000000..1fcafdc --- /dev/null +++ b/doc/io-docinfo-footer.html @@ -0,0 +1,5 @@ + diff --git a/doc/io.adoc b/doc/io.adoc index 6685efe..b8f0c2f 100644 --- a/doc/io.adoc +++ b/doc/io.adoc @@ -7,7 +7,8 @@ Distributed under the Boost Software License, Version 1.0. //// # Boost.IO -Daryle Walker, Beman Dawes +Daryle Walker, Beman Dawes, Glen Joseph Fernandes +:docinfo: private-footer :idprefix: :source-language: cpp :toc: left @@ -19,6 +20,7 @@ library. :leveloffset: +1 include::ios_state.adoc[] +include::ostream_joiner.adoc[] :leveloffset: -1 diff --git a/doc/ostream_joiner.adoc b/doc/ostream_joiner.adoc new file mode 100644 index 0000000..4a98cbf --- /dev/null +++ b/doc/ostream_joiner.adoc @@ -0,0 +1,143 @@ +//// +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +//// + +# ostream_joiner, +:toc: +:toc-title: +:idprefix: + +## Description + +The header `` provides the class template +`boost::io::ostream_joiner` which is an output iterator that writes objects to +a `std::basic_ostream` separated by a delimiter. It is an implementation of +the Library Fundamentals TS `std::ostream_joiner` which supports {cpp}03 and +higher. + +## Example + +The following program writes the contents of a vector to standard output, with +each element separated by a comma. + +``` +#include +#include +#include +#include + +int main() +{ + std::vector v; + v.push_back(2); + v.push_back(4); + v.push_back(6); + v.push_back(8); + std::copy(v.begin(), v.end(), boost::make_ostream_joiner(std::cout, ',')); +} +``` + +## Reference + +### Header Synopsis + +``` +namespace boost { +namespace io { + +template > +class ostream_joiner { +public: + typedef Char char_type; + typedef Traits traits_type; + typedef std::basic_ostream ostream_type; + typedef std::output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + ostream_joiner(ostream_type& output, const Delim& delim); + ostream_joiner(ostream_type& output, Delim&& delim); + + template + ostream_joiner& operator=(const T& value); + + ostream_joiner& operator*() noexcept; + ostream_joiner& operator++() noexcept; + ostream_joiner& operator++(int) noexcept; +}; + +template +ostream_joiner, Char, Traits> +make_ostream_joiner(std::basic_ostream& output, Delim&& delim); + +} // io +} // boost +``` + +### Constructors + +``` +ostream_joiner(ostream_type& output, const Delim& delim); +``` + +[.specification] +EFfects:: Initializes the stored reference to the stream with +`std::addressof(output)` and the stored delimiter with `delim`. + +``` +ostream_joiner(ostream_type& output, Delim&& delim); +``` + +[.specification] +EFfects:: Initializes the stored reference to the stream with +`std::addressof(output)` and the stored delimiter with `std::move(delim)`. + +### Member functions + +``` +template +ostream_joiner& operator=(const T& value); +``` + +[.specification] +Effects:: +* If the is the first call to this member function, write the stored delimiter +to the stored stream reference. +* Writes `value` to the stored stream reference. +Returns:: `*this`. + +``` +ostream_joiner& operator*() noexcept; +``` +``` +ostream_joiner& operator++() noexcept; +``` +``` +ostream_joiner& operator++(int) noexcept; +``` + +[.specification] +Returns:: `*this`. + +### Free functions + +``` +template +ostream_joiner, Char, Traits> +make_ostream_joiner(std::basic_ostream& output, Delim&& delim); +``` + +[.specification] +Returns:: `ostream_joiner, Char, Traits>(output, +std::forward(delim))`. + +## Acknowledgments + +Glen Fernandes implemented `ostream_joiner` and `make_ostream_joiner`. diff --git a/include/boost/io/ostream_joiner.hpp b/include/boost/io/ostream_joiner.hpp new file mode 100644 index 0000000..a771521 --- /dev/null +++ b/include/boost/io/ostream_joiner.hpp @@ -0,0 +1,118 @@ +/* +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BOOST_IO_OSTREAM_JOINER_HPP +#define BOOST_IO_OSTREAM_JOINER_HPP + +#include +#include +#include +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#if !defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) +#include +#endif +#include +#endif + +namespace boost { +namespace io { +namespace detail { + +#if !defined(BOOST_NO_CXX11_ADDRESSOF) +template +inline T* +osj_address(T& o) +{ + return std::addressof(o); +} +#else +template +inline T* +osj_address(T& obj) +{ + return &obj; +} +#endif + +} /* detail */ + +template > +class ostream_joiner { +public: + typedef Char char_type; + typedef Traits traits_type; + typedef std::basic_ostream ostream_type; + typedef std::output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + ostream_joiner(ostream_type& output, const Delim& delim) + : output_(detail::osj_address(output)) + , delim_(delim) + , first_(true) { } + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + ostream_joiner(ostream_type& output, Delim&& delim) + : output_(detail::osj_address(output)) + , delim_(std::move(delim)) + , first_(true) { } +#endif + + template + ostream_joiner& operator=(const T& value) { + if (!first_) { + *output_ << delim_; + } + first_ = false; + *output_ << value; + return *this; + } + + ostream_joiner& operator*() BOOST_NOEXCEPT { + return *this; + } + + ostream_joiner& operator++() BOOST_NOEXCEPT { + return *this; + } + + ostream_joiner& operator++(int) BOOST_NOEXCEPT { + return *this; + } + +private: + ostream_type* output_; + Delim delim_; + bool first_; +}; + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && \ + !defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) +template +inline ostream_joiner::type, Char, Traits> +make_ostream_joiner(std::basic_ostream& output, Delim&& delim) +{ + return ostream_joiner::type, Char, + Traits>(output, std::forward(delim)); +} +#else +template +inline ostream_joiner +make_ostream_joiner(std::basic_ostream& output, + const Delim& delim) +{ + return ostream_joiner(output, delim); +} +#endif + +} /* io */ +} /* boost */ + +#endif diff --git a/meta/libraries.json b/meta/libraries.json index 3259e18..42ad3af 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -1,9 +1,10 @@ { "key": "io", - "name": "IO State Savers", + "name": "IO", "authors": [ "Daryle Walker", - "Beman Dawes" + "Beman Dawes", + "Glen Fernandes" ], "description": "Utilities for using the standard I/O library.", "category": [ diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 8d12343..ed68f5a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -11,3 +11,5 @@ import testing ; run ios_state_unit_test.cpp ; run ios_state_test.cpp ; run quoted_manip_test.cpp ; +run ostream_joiner_test.cpp ; +run make_ostream_joiner_test.cpp ; diff --git a/test/make_ostream_joiner_test.cpp b/test/make_ostream_joiner_test.cpp new file mode 100644 index 0000000..d262f69 --- /dev/null +++ b/test/make_ostream_joiner_test.cpp @@ -0,0 +1,21 @@ +/* +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include +#include + +int main() +{ + std::ostringstream o; + boost::io::ostream_joiner j = boost::io::make_ostream_joiner(o, ','); + *j++ = 1; + *j++ = '2'; + *j++ = "3"; + BOOST_TEST_EQ(o.str(), "1,2,3"); + return boost::report_errors(); +} diff --git a/test/ostream_joiner_test.cpp b/test/ostream_joiner_test.cpp new file mode 100644 index 0000000..aa1ea44 --- /dev/null +++ b/test/ostream_joiner_test.cpp @@ -0,0 +1,116 @@ +/* +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include +#include + +void test_char_type() +{ + BOOST_TEST_TRAIT_SAME(char, + boost::io::ostream_joiner::char_type); +} + +void test_traits_type() +{ + BOOST_TEST_TRAIT_SAME(std::char_traits, + boost::io::ostream_joiner::traits_type); +} + +void test_ostream_type() +{ + BOOST_TEST_TRAIT_SAME(std::ostream, + boost::io::ostream_joiner::ostream_type); +} + +void test_iterator_category() +{ + BOOST_TEST_TRAIT_SAME(std::output_iterator_tag, + boost::io::ostream_joiner::iterator_category); +} + +void test_value_type() +{ + BOOST_TEST_TRAIT_SAME(void, + boost::io::ostream_joiner::value_type); +} + +void test_difference_type() +{ + BOOST_TEST_TRAIT_SAME(void, + boost::io::ostream_joiner::difference_type); +} + +void test_pointer() +{ + BOOST_TEST_TRAIT_SAME(void, + boost::io::ostream_joiner::pointer); +} + +void test_reference() +{ + BOOST_TEST_TRAIT_SAME(void, + boost::io::ostream_joiner::reference); +} + +void test_construct() +{ + std::ostringstream o; + boost::io::ostream_joiner j(o, ","); + BOOST_TEST(o.str().empty()); +} + +void test_assign() +{ + std::ostringstream o; + boost::io::ostream_joiner j(o, ","); + j = 1; + BOOST_TEST_EQ(o.str(), "1"); + j = '2'; + BOOST_TEST_EQ(o.str(), "1,2"); + j = "3"; + BOOST_TEST_EQ(o.str(), "1,2,3"); +} + +void test_increment() +{ + std::ostringstream o; + boost::io::ostream_joiner j(o, ","); + BOOST_TEST_EQ(&++j, &j); +} + +void test_post_increment() +{ + std::ostringstream o; + boost::io::ostream_joiner j(o, ","); + BOOST_TEST_EQ(&j++, &j); +} + +void test_value() +{ + std::ostringstream o; + boost::io::ostream_joiner j(o, ","); + BOOST_TEST_EQ(&*j, &j); +} + +int main() +{ + test_char_type(); + test_traits_type(); + test_ostream_type(); + test_iterator_category(); + test_value_type(); + test_difference_type(); + test_pointer(); + test_reference(); + test_construct(); + test_assign(); + test_increment(); + test_post_increment(); + test_value(); + return boost::report_errors(); +} From dd40a6cae4fa9adc256d8da2a0297154c3ce8be9 Mon Sep 17 00:00:00 2001 From: Glen Fernandes Date: Sat, 14 Dec 2019 17:40:39 -0500 Subject: [PATCH 2/5] Add documentation to libraries.json --- meta/libraries.json | 1 + 1 file changed, 1 insertion(+) diff --git a/meta/libraries.json b/meta/libraries.json index 42ad3af..bae4265 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -7,6 +7,7 @@ "Glen Fernandes" ], "description": "Utilities for using the standard I/O library.", + "documentation": "doc/html/io.html", "category": [ "IO" ], From f47bf89c19725a5f8590f8421bec3565bee37b41 Mon Sep 17 00:00:00 2001 From: Michael Caisse Date: Sat, 14 Dec 2019 17:30:27 -0800 Subject: [PATCH 3/5] Add Glen Fernandes as maintainer --- meta/libraries.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta/libraries.json b/meta/libraries.json index bae4265..e7626d6 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -12,6 +12,6 @@ "IO" ], "maintainers": [ - "Daryle Walker " + "Glen Fernandes " ] } From 76ee3467734f84f30cdf9c2d593839962a475e9c Mon Sep 17 00:00:00 2001 From: Glen Fernandes Date: Sat, 14 Dec 2019 20:11:54 -0500 Subject: [PATCH 4/5] Update and release the quoted manipulators --- doc/io.adoc | 5 +- doc/ios_state.adoc | 2 +- doc/ostream_joiner.adoc | 2 +- doc/quoted.adoc | 5 +- include/boost/io/detail/quoted_manip.hpp | 200 ++--------------------- include/boost/io/quoted.hpp | 152 +++++++++++++++++ meta/libraries.json | 2 +- test/Jamfile.v2 | 2 +- test/quoted_manip_test.cpp | 133 --------------- test/quoted_test.cpp | 110 +++++++++++++ 10 files changed, 286 insertions(+), 327 deletions(-) create mode 100644 include/boost/io/quoted.hpp delete mode 100644 test/quoted_manip_test.cpp create mode 100644 test/quoted_test.cpp diff --git a/doc/io.adoc b/doc/io.adoc index b8f0c2f..75478b6 100644 --- a/doc/io.adoc +++ b/doc/io.adoc @@ -13,13 +13,12 @@ Daryle Walker, Beman Dawes, Glen Joseph Fernandes :source-language: cpp :toc: left -The I/O sub-library of Boost helps segregate the large number of Boost headers. -This sub-library should contain various items to use with/for the standard I/O -library. +This library contains various utilities for the standard I/O library. :leveloffset: +1 include::ios_state.adoc[] +include::quoted.adoc[] include::ostream_joiner.adoc[] :leveloffset: -1 diff --git a/doc/ios_state.adoc b/doc/ios_state.adoc index dec2d41..1cff15a 100644 --- a/doc/ios_state.adoc +++ b/doc/ios_state.adoc @@ -6,7 +6,7 @@ Distributed under the Boost Software License, Version 1.0. (http://www.boost.org/LICENSE_1_0.txt) //// -# ios_state, +# IO State Savers, :toc: :toc-title: :idprefix: diff --git a/doc/ostream_joiner.adoc b/doc/ostream_joiner.adoc index 4a98cbf..beff8ab 100644 --- a/doc/ostream_joiner.adoc +++ b/doc/ostream_joiner.adoc @@ -6,7 +6,7 @@ Distributed under the Boost Software License, Version 1.0. (http://www.boost.org/LICENSE_1_0.txt) //// -# ostream_joiner, +# Delimited Iterators, :toc: :toc-title: :idprefix: diff --git a/doc/quoted.adoc b/doc/quoted.adoc index d82120c..5bc5639 100644 --- a/doc/quoted.adoc +++ b/doc/quoted.adoc @@ -6,7 +6,7 @@ Distributed under the Boost Software License, Version 1.0. (http://www.boost.org/LICENSE_1_0.txt) //// -# quoted, +# Quoted Manipulators, :toc: :toc-title: :idprefix: @@ -137,3 +137,6 @@ 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. + +Beman Dawes started the implementation of `quoted()` as a private detail +header. Glen Fernandes updated the implementation and also made it public. diff --git a/include/boost/io/detail/quoted_manip.hpp b/include/boost/io/detail/quoted_manip.hpp index 502f422..55a1d48 100644 --- a/include/boost/io/detail/quoted_manip.hpp +++ b/include/boost/io/detail/quoted_manip.hpp @@ -1,190 +1,18 @@ -// boost/io/quoted_manip.hpp ---------------------------------------------------------// +/* +Copyright 2010 Beman Dawes -// Copyright Beman Dawes 2010 +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) -// Distributed under the Boost Software License, Version 1.0. -// See http://www.boost.org/LICENSE_1_0.txt +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BOOST_IO_DETAIL_QUOTED_MANIP_HPP +#define BOOST_IO_DETAIL_QUOTED_MANIP_HPP -// Library home page http://www.boost.org/libs/io +/* +The new implemenation is public in +*/ +#include -//--------------------------------------------------------------------------------------// - -#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_) {} - private: - // String may be a const type, so disable the assignment operator - quoted_proxy& operator=(const quoted_proxy&); // = deleted - }; - - // 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) - { - proxy.string.clear(); - Char c; - is >> c; - if (c != proxy.delim) - { - is.unget(); - is >> proxy.string; - return is; - } - { - 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 +#endif diff --git a/include/boost/io/quoted.hpp b/include/boost/io/quoted.hpp new file mode 100644 index 0000000..44570e2 --- /dev/null +++ b/include/boost/io/quoted.hpp @@ -0,0 +1,152 @@ +/* +Copyright 2010 Beman Dawes + +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BOOST_IO_QUOTED_HPP +#define BOOST_IO_QUOTED_HPP + +#include +#include +#include +#include + +namespace boost { +namespace io { +namespace detail { + +template +class quoted_proxy { +public: + quoted_proxy(String string_, Char escape_, Char delim_) + : string(string_) + , escape(escape_) + , delim(delim_) { } + + String string; + Char escape; + Char delim; + +private: + quoted_proxy& operator=(const quoted_proxy&); +}; + +template +inline 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; +} + +template +inline std::basic_ostream& +quoted_output(std::basic_ostream& os, + const std::basic_string& string, Char escape, + Char delim) +{ + os << delim; + typename std::basic_string::const_iterator end = string.end(); + for (typename std::basic_string::const_iterator it = string.begin(); it != end; ++it) { + if (*it == delim || *it == escape) { + os << escape; + } + os << *it; + } + os << delim; + return os; +} + +template +inline std::basic_ostream& +operator<<(std::basic_ostream& os, + const quoted_proxy&, + Char>& proxy) +{ + return boost::io::detail::quoted_output(os, proxy.string, proxy.escape, + proxy.delim); +} + +template +inline std::basic_ostream& +operator<<(std::basic_ostream& os, + const quoted_proxy&, Char>& proxy) +{ + return boost::io::detail::quoted_output(os, proxy.string, proxy.escape, + proxy.delim); +} + +template +inline std::basic_istream& +operator>>(std::basic_istream& is, + const quoted_proxy&, Char>& proxy) +{ + proxy.string.clear(); + Char ch; + if (!(is >> ch).good()) { + return is; + } + if (ch != proxy.delim) { + is.unget(); + is >> proxy.string; + return is; + } + { + boost::io::ios_flags_saver ifs(is); + std::noskipws(is); + while ((is >> ch).good() && ch != proxy.delim) { + if (ch == proxy.escape && !(is >> ch).good()) { + break; + } + proxy.string.push_back(ch); + } + } + return is; +} + +} /* detail */ + +template +inline detail::quoted_proxy&, + Char> +quoted(const std::basic_string& s, Char escape='\\', + Char delim='\"') +{ + return detail::quoted_proxy&, + Char>(s, escape, delim); +} + +template +inline detail::quoted_proxy&, Char> +quoted(std::basic_string& s, Char escape='\\', + Char delim='\"') +{ + return detail::quoted_proxy&, + Char>(s, escape, delim); +} + +template +inline detail::quoted_proxy +quoted(const Char* s, Char escape='\\', Char delim='\"') +{ + return detail::quoted_proxy(s, escape, delim); +} + +} /* io */ +} /* boost */ + +#endif diff --git a/meta/libraries.json b/meta/libraries.json index e7626d6..280c708 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -6,7 +6,7 @@ "Beman Dawes", "Glen Fernandes" ], - "description": "Utilities for using the standard I/O library.", + "description": "Utilities for the standard I/O library.", "documentation": "doc/html/io.html", "category": [ "IO" diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ed68f5a..1a15cbb 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -10,6 +10,6 @@ import testing ; run ios_state_unit_test.cpp ; run ios_state_test.cpp ; -run quoted_manip_test.cpp ; +run quoted_test.cpp ; run ostream_joiner_test.cpp ; run make_ostream_joiner_test.cpp ; diff --git a/test/quoted_manip_test.cpp b/test/quoted_manip_test.cpp deleted file mode 100644 index 80e6f78..0000000 --- a/test/quoted_manip_test.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// 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::wstringstream wss; - - string r; // test results - - const string s0("foo"); - { - std::stringstream ss; - ss << quoted(s0); - ss >> r; - BOOST_TEST(r == "\"foo\""); - } - { - std::stringstream ss; - ss << quoted(s0); - ss >> quoted(r); - BOOST_TEST(r == "foo"); - } - - const string s0s("foo bar"); - { - std::stringstream ss; - ss << quoted(s0s); - ss >> r; - BOOST_TEST(r == "\"foo"); - } - { - std::stringstream ss; - ss << quoted(s0s); - ss >> quoted(r); - BOOST_TEST(r == "foo bar"); - } - - const string s1("foo\\bar, \" *"); - { - std::stringstream ss; - ss << quoted(s1); - ss >> r; - BOOST_TEST(r == "\"foo\\\\bar,"); - } - { - std::stringstream ss; - ss << quoted("foo\\bar, \" *"); - ss >> r; - BOOST_TEST(r == "\"foo\\\\bar,"); - } - { - std::stringstream ss; - ss << quoted(s1); - ss >> quoted(r); - BOOST_TEST(r == s1); - } - { - std::stringstream ss; - ss << quoted(s1.c_str()); - ss >> quoted(r); - BOOST_TEST(r == s1); - } - - string s2("'Jack & Jill'"); - { - std::stringstream ss; - ss << quoted(s2, '&', '\''); - ss >> quoted(r, '&', '\''); - BOOST_TEST(r == s2); - } - - wstring ws1(L"foo$bar, \" *"); - wstring wr; // test results - { - std::wstringstream wss; - wss << quoted(ws1, L'$'); - wss >> quoted(wr, L'$'); - BOOST_TEST(wr == ws1); - } - - const string s3("const string"); - { - std::stringstream ss; - ss << quoted(s3); - ss >> quoted(r); - BOOST_TEST(r == s3); - } - { - // missing end delimiter test - std::stringstream ss; - ss << "\"abc"; // load ss with faulty quoting - ss >> quoted(r); // this loops if istream error/eof not detected - BOOST_TEST(r == "abc"); - } - { - // no initial delmiter test - std::stringstream ss; - ss << "abc"; - ss >> quoted(r); - BOOST_TEST(r == "abc"); - } - { - // no initial delmiter, space in ss - std::stringstream ss; - ss << "abc def"; - ss >> quoted(r); - BOOST_TEST(r == "abc"); - } - - // these should fail to compile because the arguments are const: - // ss >> quoted(s1); - // ss >> quoted("foo"); - - return boost::report_errors(); -} diff --git a/test/quoted_test.cpp b/test/quoted_test.cpp new file mode 100644 index 0000000..2ca0a25 --- /dev/null +++ b/test/quoted_test.cpp @@ -0,0 +1,110 @@ +/* +Copyright 2010 Beman Dawes + +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include +#include +#include + +int main() +{ + const std::string s0("foo"); + std::string r; + { + std::stringstream ss; + ss << boost::io::quoted(s0); + ss >> r; + BOOST_TEST(r == "\"foo\""); + } + { + std::stringstream ss; + ss << boost::io::quoted(s0); + ss >> boost::io::quoted(r); + BOOST_TEST(r == "foo"); + } + const std::string s0s("foo bar"); + { + std::stringstream ss; + ss << boost::io::quoted(s0s); + ss >> r; + BOOST_TEST(r == "\"foo"); + } + { + std::stringstream ss; + ss << boost::io::quoted(s0s); + ss >> boost::io::quoted(r); + BOOST_TEST(r == "foo bar"); + } + const std::string s1("foo\\bar, \" *"); + { + std::stringstream ss; + ss << boost::io::quoted(s1); + ss >> r; + BOOST_TEST(r == "\"foo\\\\bar,"); + } + { + std::stringstream ss; + ss << boost::io::quoted("foo\\bar, \" *"); + ss >> r; + BOOST_TEST(r == "\"foo\\\\bar,"); + } + { + std::stringstream ss; + ss << boost::io::quoted(s1); + ss >> boost::io::quoted(r); + BOOST_TEST(r == s1); + } + { + std::stringstream ss; + ss << boost::io::quoted(s1.c_str()); + ss >> boost::io::quoted(r); + BOOST_TEST(r == s1); + } + std::string s2("'Jack & Jill'"); + { + std::stringstream ss; + ss << boost::io::quoted(s2, '&', '\''); + ss >> boost::io::quoted(r, '&', '\''); + BOOST_TEST(r == s2); + } + const std::wstring ws1(L"foo$bar, \" *"); + std::wstring wr; + { + std::wstringstream wss; + wss << boost::io::quoted(ws1, L'$'); + wss >> boost::io::quoted(wr, L'$'); + BOOST_TEST(wr == ws1); + } + const std::string s3("const string"); + { + std::stringstream ss; + ss << boost::io::quoted(s3); + ss >> boost::io::quoted(r); + BOOST_TEST(r == s3); + } + { + std::stringstream ss; + ss << "\"abc"; + ss >> boost::io::quoted(r); + BOOST_TEST(r == "abc"); + } + { + std::stringstream ss; + ss << "abc"; + ss >> boost::io::quoted(r); + BOOST_TEST(r == "abc"); + } + { + std::stringstream ss; + ss << "abc def"; + ss >> boost::io::quoted(r); + BOOST_TEST(r == "abc"); + } + return boost::report_errors(); +} From 16913b5484bbd0ad468b9a10a555b807f2ca6665 Mon Sep 17 00:00:00 2001 From: Glen Fernandes Date: Sat, 14 Dec 2019 23:10:30 -0500 Subject: [PATCH 5/5] Move my ostream_string function to the IO library --- doc/io.adoc | 1 + doc/ostream_put.adoc | 73 +++++++++++++++++ include/boost/io/ostream_put.hpp | 97 ++++++++++++++++++++++ test/Jamfile.v2 | 1 + test/ostream_put_test.cpp | 136 +++++++++++++++++++++++++++++++ 5 files changed, 308 insertions(+) create mode 100644 doc/ostream_put.adoc create mode 100644 include/boost/io/ostream_put.hpp create mode 100644 test/ostream_put_test.cpp diff --git a/doc/io.adoc b/doc/io.adoc index 75478b6..72b200b 100644 --- a/doc/io.adoc +++ b/doc/io.adoc @@ -20,6 +20,7 @@ This library contains various utilities for the standard I/O library. include::ios_state.adoc[] include::quoted.adoc[] include::ostream_joiner.adoc[] +include::ostream_put.adoc[] :leveloffset: -1 diff --git a/doc/ostream_put.adoc b/doc/ostream_put.adoc new file mode 100644 index 0000000..74b536a --- /dev/null +++ b/doc/ostream_put.adoc @@ -0,0 +1,73 @@ +//// +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +//// + +# Insert Formatted Output, +:toc: +:toc-title: +:idprefix: + +## Description + +The header `` provides the function template +`boost::io::ostream_put` for formatted output that satisfies the requirements +of [ostream.formatted.reqmts]. + +## Example + +The inserter for class template `basic_string_view` could be implemented as +follows: + +``` +template +std::basic_ostream& +operator<<(std::basic_ostream& os, + const basic_string_view& str) +{ + return boost::io::ostream_put(os, str.data(), str.size()); +} +``` + +## Reference + +### Header Synopsis + +``` +namespace boost { +namespace io { + +template +std::basic_ostream& +ostream_put(std::basic_ostream& os, + const charT* data, std::size_t size); + +} // io +} // boost +``` + +### Free functions + +``` +template +std::basic_ostream& +ostream_put(std::basic_ostream& os, + const charT* data, std::size_t size); +``` + +[.specification] +Effects:: Behaves like a formatted inserter (as described in +[ostream.formatted.reqmts]) of `os`. Creates a character sequence `seq` of size +characters starting at `data`, each widened using `os.widen()` +([basic.ios.members]). Determines padding for `seq` as described in +[ostream.formatted.reqmts]. Inserts `seq` into `os`. Calls `width(0)`. +Returns:: `os`. + +## Acknowledgments + +Glen Fernandes updated the implementation of the `basic_string_ref` and +`basic_string_view` stream insertion operators to write directly to the +`basic_streambuf` and refactored that functionality into this common utility. diff --git a/include/boost/io/ostream_put.hpp b/include/boost/io/ostream_put.hpp new file mode 100644 index 0000000..5ed5753 --- /dev/null +++ b/include/boost/io/ostream_put.hpp @@ -0,0 +1,97 @@ +/* +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BOOST_IO_OSTREAM_PUT_HPP +#define BOOST_IO_OSTREAM_PUT_HPP + +#include +#include +#include + +namespace boost { +namespace io { +namespace detail { + +template +inline std::size_t +osp_put(std::basic_ostream& os, const charT* data, + std::size_t size) +{ + return static_cast(os.rdbuf()->sputn(data, size)); +} + +template +inline bool +osp_fill(std::basic_ostream& os, std::size_t size) +{ + charT c = os.fill(); + charT fill[] = { c, c, c, c, c, c, c, c }; + enum { + chunk = sizeof fill / sizeof(charT) + }; + for (; size > chunk; size -= chunk) { + if (boost::io::detail::osp_put(os, fill, chunk) != chunk) { + return false; + } + } + return boost::io::detail::osp_put(os, fill, size) == size; +} + +template +class osp_guard { +public: + explicit osp_guard(std::basic_ostream& os) BOOST_NOEXCEPT + : os_(&os) { } + ~osp_guard() BOOST_NOEXCEPT_IF(false) { + if (os_) { + os_->setstate(std::basic_ostream::badbit); + } + } + void release() BOOST_NOEXCEPT { + os_ = 0; + } +private: + osp_guard(const osp_guard&); + osp_guard& operator=(const osp_guard&); + std::basic_ostream* os_; +}; + +} /* detail */ + +template +inline std::basic_ostream& +ostream_put(std::basic_ostream& os, const charT* data, + std::size_t size) +{ + typedef std::basic_ostream stream; + detail::osp_guard guard(os); + typename stream::sentry entry(os); + if (entry) { + std::size_t width = static_cast(os.width()); + if (width <= size) { + if (detail::osp_put(os, data, size) != size) { + return os; + } + } else if ((os.flags() & stream::adjustfield) == stream::left) { + if (detail::osp_put(os, data, size) != size || + !detail::osp_fill(os, width - size)) { + return os; + } + } else if (!detail::osp_fill(os, width - size) || + detail::osp_put(os, data, size) != size) { + return os; + } + os.width(0); + } + guard.release(); + return os; +} + +} /* io */ +} /* boost */ + +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 1a15cbb..4a26c6d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -13,3 +13,4 @@ run ios_state_test.cpp ; run quoted_test.cpp ; run ostream_joiner_test.cpp ; run make_ostream_joiner_test.cpp ; +run ostream_put_test.cpp ; diff --git a/test/ostream_put_test.cpp b/test/ostream_put_test.cpp new file mode 100644 index 0000000..3ce978d --- /dev/null +++ b/test/ostream_put_test.cpp @@ -0,0 +1,136 @@ +/* +Copyright 2019 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include +#include +#include + +int main() +{ + { + std::ostringstream os; + os.width(1); + os.fill('.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + boost::io::ostream_put(os, "xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "xy"); + } + { + std::wostringstream os; + os.width(1); + os.fill('.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + boost::io::ostream_put(os, L"xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"xy"); + } + { + std::ostringstream os; + os.width(1); + os.fill('.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + boost::io::ostream_put(os, "xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "xy"); + } + { + std::wostringstream os; + os.width(1); + os.fill('.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + boost::io::ostream_put(os, L"xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"xy"); + } + { + std::ostringstream os; + os.width(4); + os.fill('.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + boost::io::ostream_put(os, "xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "xy.."); + } + { + std::wostringstream os; + os.width(4); + os.fill(L'.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + boost::io::ostream_put(os, L"xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"xy.."); + } + { + std::ostringstream os; + os.width(4); + os.fill('.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + boost::io::ostream_put(os, "xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "..xy"); + } + { + std::wostringstream os; + os.width(4); + os.fill(L'.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + boost::io::ostream_put(os, L"xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"..xy"); + } + { + std::ostringstream os; + os.width(12); + os.fill('.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + boost::io::ostream_put(os, "xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "xy.........."); + } + { + std::wostringstream os; + os.width(12); + os.fill(L'.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + boost::io::ostream_put(os, L"xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"xy.........."); + } + { + std::ostringstream os; + os.width(12); + os.fill('.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + boost::io::ostream_put(os, "xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "..........xy"); + } + { + std::wostringstream os; + os.width(12); + os.fill(L'.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + boost::io::ostream_put(os, L"xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"..........xy"); + } + return boost::report_errors(); +}