diff --git a/doc/boost_range.qbk b/doc/boost_range.qbk index 126ec62..406d1a8 100644 --- a/doc/boost_range.qbk +++ b/doc/boost_range.qbk @@ -63,6 +63,8 @@ [def __counting_range__ [link range.reference.ranges.counting_range `counting_range`]] [def __irange__ [link range.reference.ranges.irange `irange`]] [def __istream_range__ [link range.reference.ranges.istream_range `istream_range`]] +[def __combine__ [link range.reference.utilities.combine `combine`]] +[def __separated__ [link range.reference.utilities.separated `separated`]] [def __join__ [link range.reference.utilities.join `join`]] [def __range_adaptors__ [link range.reference.adaptors Range adaptors]] diff --git a/doc/reference/utilities.qbk b/doc/reference/utilities.qbk index 2a18f35..a86a3f4 100644 --- a/doc/reference/utilities.qbk +++ b/doc/reference/utilities.qbk @@ -10,6 +10,7 @@ Having an abstraction that encapsulates a pair of iterators is very useful. The * Class `iterator_range` * Class `sub_range` * Function `combine` +* Function `separated` * Function `join` The `iterator_range` class is templated on an __forward_traversal_iterator__ and should be used whenever fairly general code is needed. The `sub_range` class is templated on an __forward_range__ and it is less general, but a bit easier to use since its template argument is easier to specify. The biggest difference is, however, that a `sub_range` can propagate constness because it knows what a corresponding `const_iterator` is. @@ -404,6 +405,55 @@ For the mutable version: The expression `join(irange(0,5), irange(5,10))` would evaluate to a range representing an integer range `[0,10)` +[endsect] + +[section:separated Function separated] + +The separated function allows output streaming a range while writing a separator +between each element. + +[h4 Synopsis] + +`` +template +class output_stream_writer +{ + // ... unspecified +}; + +template +std::basic_ostream& operator<<( + std::basic_ostream& out, + const output_stream_writer& writer); + +template +boost::range::output_stream_writer< + typename boost::range_iterator::type, + Separator +> separated(const Range& rng, Separator separator); +`` + +[h4 Example] + +`` +#include +#include +#include + +int main(int, const char*[]) +{ + std::vector v; + for (int i = 0; i < 5; ++i) + v.push_back(v); + + std::cout << '{' << boost::range::separated(v, ',') << '}' << std::endl; + + return 0; +} +`` + +Produces the output: `{0,1,2,3,4}` + [endsect] [endsect] diff --git a/include/boost/range/separated.hpp b/include/boost/range/separated.hpp new file mode 100644 index 0000000..556441e --- /dev/null +++ b/include/boost/range/separated.hpp @@ -0,0 +1,94 @@ +// Boost.Range library +// +// Copyright Neil Groves 2014. +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see http://www.boost.org/libs/range/ +// +#ifndef BOOST_RANGE_SEPARATED_HPP_INCLUDED +#define BOOST_RANGE_SEPARATED_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace boost +{ + namespace range_detail + { + +template +class output_stream_writer +{ +public: + output_stream_writer(Iter first, Iter last, Separator separator) + : m_first(first) + , m_last(last) + , m_separator(separator) + { + } + + template + void write(OStream& out) const + { + write_impl(out, m_first, m_last, m_separator); + } + +private: + template + static void write_impl( + OStream& out, Iter first, Iter last, Separator separator) + { + if (first != last) + { + out << *first; + for (++first; first != last; ++first) + { + out << separator << *first; + } + } + } + + Iter m_first; + Iter m_last; + Separator m_separator; +}; + +template +std::basic_ostream& +operator<<( + std::basic_ostream& out, + const output_stream_writer& writer) +{ + writer.write(out); + return out; +} + + } // namespace range_detail + + namespace range + { + +template +inline range_detail::output_stream_writer< + typename range_iterator::type, + Separator +> +separated(const Range& rng, Separator separator) +{ + BOOST_RANGE_CONCEPT_ASSERT((SinglePassRangeConcept)); + return range_detail::output_stream_writer< + typename range_iterator::type, + Separator + >(boost::begin(rng), boost::end(rng), separator); +} + + } // namespace range +} // namespace boost + +#endif // include guard diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 59251f1..4354c3f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -179,6 +179,7 @@ test-suite range : [ range-test reverse_iterator ] [ range-test reverse_result_iterator ] [ range-test reversible_range ] + [ range-test separated ] [ range-test size_type ] [ range-test std_container ] [ range-test string ] diff --git a/test/separated.cpp b/test/separated.cpp new file mode 100644 index 0000000..680558d --- /dev/null +++ b/test/separated.cpp @@ -0,0 +1,107 @@ +// Boost.Range library +// +// Copyright Neil Groves 2014. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see http://www.boost.org/libs/range/ +// +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost_range_test +{ + namespace + { + +void test_separated1() +{ + std::vector v; + for (boost::int32_t i = 0; i < 10; ++i) + v.push_back(i); + + std::ostringstream out; + out << '{' << boost::range::separated(v, ',') << '}'; + + BOOST_CHECK_EQUAL(out.str(), "{0,1,2,3,4,5,6,7,8,9}"); +} + +void test_separated2() +{ + std::vector v; + v.push_back(3); + + std::ostringstream out; + out << '{' << boost::range::separated(v, ',') << '}'; + + BOOST_CHECK_EQUAL(out.str(), "{3}"); +} + +void test_separated3() +{ + std::vector v; + + std::ostringstream out; + out << '{' << boost::range::separated(v, ',') << '}'; + + BOOST_CHECK_EQUAL(out.str(), "{}"); +} + +void test_separated4() +{ + std::vector v; + for (boost::int32_t i = 0; i < 5; ++i) + v.push_back(i); + + std::ostringstream out; + out << '{' << boost::range::separated(v, "::") << '}'; + + BOOST_CHECK_EQUAL(out.str(), "{0::1::2::3::4}"); +} + +struct udt_separator +{ +}; + +template +inline std::basic_ostream& +operator<<(std::basic_ostream& out, udt_separator) +{ + return out << "[sep]"; +} + +void test_separated5() +{ + std::vector v; + for (boost::int32_t i = 0; i < 5; ++i) + v.push_back(i); + + std::ostringstream out; + out << '{' << boost::range::separated(v, udt_separator()) << '}'; + + BOOST_CHECK_EQUAL(out.str(), "{0[sep]1[sep]2[sep]3[sep]4}"); +} + + } // anonymous namespace +} // namespace boost_range_test + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[] ) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE( "Boost.Range separated test suite" ); + + test->add(BOOST_TEST_CASE(&boost_range_test::test_separated1)); + test->add(BOOST_TEST_CASE(&boost_range_test::test_separated2)); + test->add(BOOST_TEST_CASE(&boost_range_test::test_separated3)); + test->add(BOOST_TEST_CASE(&boost_range_test::test_separated4)); + test->add(BOOST_TEST_CASE(&boost_range_test::test_separated5)); + + return test; +}