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(); +}