Search code examples
c++iteratorc++11ostream

Don't print trailing delimiter stream_iterator C++


What is the most elegant way, in your opinion, to print to std::cout using std::ostream_iterator in C++11 and avoid printing a trailing delimeter?

The object I'm printing has bidirectional iterators, but not random access iterators.

std::list<double> x{1,2,3,4,5,6};
std::copy(x.begin(), std::prev(x.end()),
                std::ostream_iterator<int>(std::cout, ",") );
if ( x.size() != 0 )
  std::cout << *(--x.end()) << std::endl;

Solution

  • Here's one of my favorites, but it doesn't use std::ostream_iterator:

    #include <iterator>
    #include <string>
    #include <iosfwd>
    
    template <class C>
    auto
    print(std::ostream& os, const C& c,
          const std::string& delim = std::string(", "),
          const std::string& open_brace = std::string("{"),
          const std::string& close_brace = std::string("}")
         ) -> decltype(std::begin(c), std::end(c), os)
    {
        os << open_brace;
        auto i = std::begin(c);
        auto e = std::end(c);
        if (i != e)
        {
            os << *i;
            for (++i; i != e; ++i)
                os << delim << *i;
        }
        os << close_brace;
        return os;
    }
    
    #include <list>
    #include <iostream>
    
    int main()
    {
        std::list<double> x{1,2,3,4,5,6};
        print(std::cout, x) << '\n';
    }
    
    {1, 2, 3, 4, 5, 6}
    

    Update

    Oliver goaded me into a challenge I couldn't resist. :-)

    #include <iterator>
    #include <string>
    #include <iosfwd>
    
    namespace my {
    
    template <class C>
    auto
    print(std::ostream& os, const C& c,
          const std::string& delim = std::string(", "),
          const std::string& open_brace = std::string("{"),
          const std::string& close_brace = std::string("}")
         ) -> decltype(std::begin(c), std::end(c), os);
    
    template <class C,
               typename std::enable_if
                        <
                           !std::is_same<C, std::string>::value,
                        bool>::type = false
             >
    inline
    auto
    operator<< (std::ostream& os, const C& c) -> decltype(print(os, c))
    {
        return print(os, c);
    }
    
    template <class C>
    auto
    print(std::ostream& os, const C& c,
          const std::string& delim,
          const std::string& open_brace,
          const std::string& close_brace
         ) -> decltype(std::begin(c), std::end(c), os)
    {
        os << open_brace;
        auto i = std::begin(c);
        auto e = std::end(c);
        if (i != e)
        {
            os << *i;
            for (++i; i != e; ++i)
                os << delim << *i;
        }
        os << close_brace;
        return os;
    }
    
    }
    
    #include <list>
    #include <forward_list>
    #include <iostream>
    
    int main()
    {
        std::forward_list<std::list<double>> x{{}, {3, 2, 1}, {1,2,3,4,5,6}};
        my::print(std::cout, x) << '\n';
    }
    
    {{}, {3, 2, 1}, {1, 2, 3, 4, 5, 6}}
    

    It isn't perfect, but it was fun. :-) There's probably a better way to do it that would propagate the custom delimiter and braces more faithfully.