Implement ostream_joiner

This commit is contained in:
Glen Fernandes
2019-12-14 07:56:15 -05:00
parent a6b31015c6
commit 2dc89e1eee
8 changed files with 411 additions and 3 deletions

View File

@ -0,0 +1,5 @@
<style>
.specification {
margin-left: 2em;
}
</style>

View File

@ -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 <glenjofe@gmail.com>
: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

143
doc/ostream_joiner.adoc Normal file
View File

@ -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, <boost/io/ostream_joiner.hpp>
:toc:
:toc-title:
:idprefix:
## Description
The header `<boost/io/ostream_joiner.hpp>` 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 <boost/io/ostream_joiner.hpp>
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> 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 Delim, class Char = char,
class Traits = std::char_traits<Char> >
class ostream_joiner {
public:
typedef Char char_type;
typedef Traits traits_type;
typedef std::basic_ostream<Char, Traits> 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<class T>
ostream_joiner& operator=(const T& value);
ostream_joiner& operator*() noexcept;
ostream_joiner& operator++() noexcept;
ostream_joiner& operator++(int) noexcept;
};
template<class Char, class Traits, class Delim>
ostream_joiner<std::decay_t<Delim>, Char, Traits>
make_ostream_joiner(std::basic_ostream<Char, Traits>& 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<class T>
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<class Char, class Traits, class Delim>
ostream_joiner<decay_t<Delim>, Char, Traits>
make_ostream_joiner(std::basic_ostream<Char, Traits>& output, Delim&& delim);
```
[.specification]
Returns:: `ostream_joiner<std::decay_t<Delim>, Char, Traits>(output,
std::forward<Delim>(delim))`.
## Acknowledgments
Glen Fernandes implemented `ostream_joiner` and `make_ostream_joiner`.

View File

@ -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 <boost/config.hpp>
#include <ostream>
#include <string>
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
#if !defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS)
#include <type_traits>
#endif
#include <utility>
#endif
namespace boost {
namespace io {
namespace detail {
#if !defined(BOOST_NO_CXX11_ADDRESSOF)
template<class T>
inline T*
osj_address(T& o)
{
return std::addressof(o);
}
#else
template<class T>
inline T*
osj_address(T& obj)
{
return &obj;
}
#endif
} /* detail */
template<class Delim, class Char = char,
class Traits = std::char_traits<Char> >
class ostream_joiner {
public:
typedef Char char_type;
typedef Traits traits_type;
typedef std::basic_ostream<Char, Traits> 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<class T>
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<class Char, class Traits, class Delim>
inline ostream_joiner<typename std::decay<Delim>::type, Char, Traits>
make_ostream_joiner(std::basic_ostream<Char, Traits>& output, Delim&& delim)
{
return ostream_joiner<typename std::decay<Delim>::type, Char,
Traits>(output, std::forward<Delim>(delim));
}
#else
template<class Char, class Traits, class Delim>
inline ostream_joiner<Delim, Char, Traits>
make_ostream_joiner(std::basic_ostream<Char, Traits>& output,
const Delim& delim)
{
return ostream_joiner<Delim, Char, Traits>(output, delim);
}
#endif
} /* io */
} /* boost */
#endif

View File

@ -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": [

View File

@ -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 ;

View File

@ -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 <boost/core/lightweight_test.hpp>
#include <boost/io/ostream_joiner.hpp>
#include <sstream>
int main()
{
std::ostringstream o;
boost::io::ostream_joiner<char> 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();
}

View File

@ -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 <boost/core/lightweight_test_trait.hpp>
#include <boost/io/ostream_joiner.hpp>
#include <sstream>
void test_char_type()
{
BOOST_TEST_TRAIT_SAME(char,
boost::io::ostream_joiner<const char*>::char_type);
}
void test_traits_type()
{
BOOST_TEST_TRAIT_SAME(std::char_traits<char>,
boost::io::ostream_joiner<const char*>::traits_type);
}
void test_ostream_type()
{
BOOST_TEST_TRAIT_SAME(std::ostream,
boost::io::ostream_joiner<const char*>::ostream_type);
}
void test_iterator_category()
{
BOOST_TEST_TRAIT_SAME(std::output_iterator_tag,
boost::io::ostream_joiner<const char*>::iterator_category);
}
void test_value_type()
{
BOOST_TEST_TRAIT_SAME(void,
boost::io::ostream_joiner<const char*>::value_type);
}
void test_difference_type()
{
BOOST_TEST_TRAIT_SAME(void,
boost::io::ostream_joiner<const char*>::difference_type);
}
void test_pointer()
{
BOOST_TEST_TRAIT_SAME(void,
boost::io::ostream_joiner<const char*>::pointer);
}
void test_reference()
{
BOOST_TEST_TRAIT_SAME(void,
boost::io::ostream_joiner<const char*>::reference);
}
void test_construct()
{
std::ostringstream o;
boost::io::ostream_joiner<const char*> j(o, ",");
BOOST_TEST(o.str().empty());
}
void test_assign()
{
std::ostringstream o;
boost::io::ostream_joiner<const char*> 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<const char*> j(o, ",");
BOOST_TEST_EQ(&++j, &j);
}
void test_post_increment()
{
std::ostringstream o;
boost::io::ostream_joiner<const char*> j(o, ",");
BOOST_TEST_EQ(&j++, &j);
}
void test_value()
{
std::ostringstream o;
boost::io::ostream_joiner<const char*> 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();
}