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