From dabf53a703f70ed6bdfb6b981fc326b9e9a10226 Mon Sep 17 00:00:00 2001 From: Glen Fernandes Date: Thu, 18 Apr 2019 16:30:40 -0400 Subject: [PATCH] Refactor stream write functionality into a standalone utility --- doc/Jamfile.v2 | 15 ++- doc/ostream_string.qbk | 62 +++++++++++ include/boost/utility/ostream_string.hpp | 95 ++++++++++++++++ include/boost/utility/string_ref.hpp | 68 +----------- include/boost/utility/string_view.hpp | 68 +----------- index.html | 1 + test/Jamfile.v2 | 2 + test/ostream_string_test.cpp | 136 +++++++++++++++++++++++ 8 files changed, 314 insertions(+), 133 deletions(-) create mode 100644 doc/ostream_string.qbk create mode 100644 include/boost/utility/ostream_string.hpp create mode 100644 test/ostream_string_test.cpp diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index dda8f39..b2d5229 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -99,6 +99,19 @@ boostbook standalone_declval generate.section.toc.level=1 ; +xml ostream_string : ostream_string.qbk ; +boostbook standalone_ostream_string + : + ostream_string + : + root.filename=ostream_string + chunk.section.depth=0 + chunk.first.sections=0 + toc.section.depth=1 + toc.max.depth=1 + generate.section.toc.level=1 + ; + xml string_ref : string_ref.qbk ; boostbook standalone_string_ref : @@ -123,5 +136,5 @@ alias boostdoc ; explicit boostdoc ; alias boostrelease : standalone_base_from_member standalone_compressed_pair - standalone_declval standalone_string_ref ; + standalone_declval standalone_ostream_string standalone_string_ref ; explicit boostrelease ; diff --git a/doc/ostream_string.qbk b/doc/ostream_string.qbk new file mode 100644 index 0000000..a99ad0c --- /dev/null +++ b/doc/ostream_string.qbk @@ -0,0 +1,62 @@ +[/ +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) +] + +[article ostream_string +[quickbook 1.5] +[authors [Fernandes, Glen]] +[copyright 2019 Glen Joseph Fernandes] +[license Distributed under the Boost Software License, Version 1.0.]] + +[section Overview] + +The header provides the function template +boost::ostream_string for formatted output that satisfies the requirements of +\[ostream.formatted.reqmts\]. + +[endsect] + +[section Examples] + +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::ostream_string(os, str.data(), str.size()); +} +``` + +[endsect] + +[section Reference] + +``` +namespace boost { + +template +std::basic_ostream& +ostream_string(std::basic_ostream& os, + const charT* data, std::size_t size); + +} /* boost */ +``` + +[variablelist +[[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`.]]] + +[endsect] diff --git a/include/boost/utility/ostream_string.hpp b/include/boost/utility/ostream_string.hpp new file mode 100644 index 0000000..8722e58 --- /dev/null +++ b/include/boost/utility/ostream_string.hpp @@ -0,0 +1,95 @@ +/* +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_UTILITY_OSTREAM_STRING_HPP +#define BOOST_UTILITY_OSTREAM_STRING_HPP + +#include +#include +#include + +namespace boost { +namespace detail { + +template +inline std::size_t +oss_put(std::basic_ostream& os, const charT* data, + std::size_t size) +{ + return static_cast(os.rdbuf()->sputn(data, size)); +} + +template +inline bool +oss_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::detail::oss_put(os, fill, chunk) != chunk) { + return false; + } + } + return boost::detail::oss_put(os, fill, size) == size; +} + +template +class oss_guard { +public: + explicit oss_guard(std::basic_ostream& os) BOOST_NOEXCEPT + : os_(&os) { } + ~oss_guard() BOOST_NOEXCEPT_IF(false) { + if (os_) { + os_->setstate(std::basic_ostream::badbit); + } + } + void release() BOOST_NOEXCEPT { + os_ = 0; + } +private: + oss_guard(const oss_guard&); + oss_guard& operator=(const oss_guard&); + std::basic_ostream* os_; +}; + +} /* detail */ + +template +inline std::basic_ostream& +ostream_string(std::basic_ostream& os, const charT* data, + std::size_t size) +{ + typedef std::basic_ostream stream; + detail::oss_guard guard(os); + typename stream::sentry entry(os); + if (entry) { + std::size_t width = static_cast(os.width()); + if (width <= size) { + if (detail::oss_put(os, data, size) != size) { + return os; + } + } else if ((os.flags() & stream::adjustfield) == stream::left) { + if (detail::oss_put(os, data, size) != size || + !detail::oss_fill(os, width - size)) { + return os; + } + } else if (!detail::oss_fill(os, width - size) || + detail::oss_put(os, data, size) != size) { + return os; + } + os.width(0); + } + guard.release(); + return os; +} + +} /* boost */ + +#endif diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index 36f4fc8..999f1c3 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -422,76 +423,11 @@ namespace boost { return basic_string_ref(x) >= y; } - namespace detail { - - template - inline std::size_t sr_os_put(std::basic_ostream& os, - const charT* data, std::size_t size) { - return static_cast(os.rdbuf()->sputn(data, size)); - } - - template - inline bool sr_os_fill(std::basic_ostream& os, - std::size_t size) { - enum { chunk = 8 }; - charT fill[chunk]; - std::fill_n(fill, static_cast(chunk), os.fill()); - for (; size > chunk; size -= chunk) { - if (detail::sr_os_put(os, fill, chunk) != chunk) { - return false; - } - } - return detail::sr_os_put(os, fill, size) == size; - } - - template - class sr_os_holder { - public: - explicit sr_os_holder(std::basic_ostream& os) - : os_(&os) { } - ~sr_os_holder() BOOST_NOEXCEPT_IF(false) { - if (os_) { - os_->setstate(std::basic_ostream::badbit); - } - } - void release() { - os_ = 0; - } - private: - sr_os_holder(const sr_os_holder&); - sr_os_holder& operator=(const sr_os_holder&); - std::basic_ostream* os_; - }; - - } // namespace detail - // Inserter template inline std::basic_ostream& operator<<(std::basic_ostream& os, const basic_string_ref& str) { - typedef std::basic_ostream stream; - detail::sr_os_holder hold(os); - typename stream::sentry entry(os); - if (entry) { - std::size_t width = static_cast(os.width()); - std::size_t size = str.size(); - if (width <= size) { - if (detail::sr_os_put(os, str.data(), size) != size) { - return os; - } - } else if ((os.flags() & stream::adjustfield) == stream::left) { - if (detail::sr_os_put(os, str.data(), size) != size || - !detail::sr_os_fill(os, width - size)) { - return os; - } - } else if (!detail::sr_os_fill(os, width - size) || - detail::sr_os_put(os, str.data(), size) != size) { - return os; - } - os.width(0); - } - hold.release(); - return os; + return boost::ostream_string(os, str.data(), str.size()); } #if 0 diff --git a/include/boost/utility/string_view.hpp b/include/boost/utility/string_view.hpp index 5cab012..1677e9f 100644 --- a/include/boost/utility/string_view.hpp +++ b/include/boost/utility/string_view.hpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -572,77 +573,12 @@ namespace boost { return basic_string_view(x) >= y; } - namespace detail { - - template - inline std::size_t sv_os_put(std::basic_ostream& os, - const charT* data, std::size_t size) { - return static_cast(os.rdbuf()->sputn(data, size)); - } - - template - inline bool sv_os_fill(std::basic_ostream& os, - std::size_t size) { - enum { chunk = 8 }; - charT fill[chunk]; - std::fill_n(fill, static_cast(chunk), os.fill()); - for (; size > chunk; size -= chunk) { - if (detail::sv_os_put(os, fill, chunk) != chunk) { - return false; - } - } - return detail::sv_os_put(os, fill, size) == size; - } - - template - class sv_os_holder { - public: - explicit sv_os_holder(std::basic_ostream& os) - : os_(&os) { } - ~sv_os_holder() BOOST_NOEXCEPT_IF(false) { - if (os_) { - os_->setstate(std::basic_ostream::badbit); - } - } - void release() { - os_ = 0; - } - private: - sv_os_holder(const sv_os_holder&); - sv_os_holder& operator=(const sv_os_holder&); - std::basic_ostream* os_; - }; - - } // namespace detail - // Inserter template inline std::basic_ostream& operator<<(std::basic_ostream& os, const basic_string_view& str) { - typedef std::basic_ostream stream; - detail::sv_os_holder hold(os); - typename stream::sentry entry(os); - if (entry) { - std::size_t width = static_cast(os.width()); - std::size_t size = str.size(); - if (width <= size) { - if (detail::sv_os_put(os, str.data(), size) != size) { - return os; - } - } else if ((os.flags() & stream::adjustfield) == stream::left) { - if (detail::sv_os_put(os, str.data(), size) != size || - !detail::sv_os_fill(os, width - size)) { - return os; - } - } else if (!detail::sv_os_fill(os, width - size) || - detail::sv_os_put(os, str.data(), size) != size) { - return os; - } - os.width(0); - } - hold.release(); - return os; + return boost::ostream_string(os, str.data(), str.size()); } #if 0 diff --git a/index.html b/index.html index 57fa56f..7dcdd64 100644 --- a/index.html +++ b/index.html @@ -31,6 +31,7 @@ result_of
throw_exception
utility
+ ostream_string
string_ref
value_init

diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 739edc0..d0277ef 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -42,3 +42,5 @@ compile-fail value_init_test_fail2.cpp ; compile-fail value_init_test_fail3.cpp ; compile-fail initialized_test_fail1.cpp ; compile-fail initialized_test_fail2.cpp ; + +run ostream_string_test.cpp ; diff --git a/test/ostream_string_test.cpp b/test/ostream_string_test.cpp new file mode 100644 index 0000000..e6145b3 --- /dev/null +++ b/test/ostream_string_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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(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::ostream_string(os, L"xy", 2); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"..........xy"); + } + return boost::report_errors(); +}