Question

Does Boost Ranges have a built-in way to cout the elements easily, for example separated by comma or space?

I am of course aware that I could loop over them and print them separately, but it seems to me that this should be built in somehow (like printing a vector in scripting languages).

In an example program I saw, the author copied the range to cout:

boost::copy(range, ostream_iterator(cout, " "))

Looks ugly to me. Is this idiomatic?

EDIT: A solution for std iterators would also be acceptable.

EDIT2: What I want to do is this:

cout << range;

But I don't think that works. So what I am hoping for is something like this (Python inspired):

cout << range.join(", ");

or perhaps with a range adaptor.

cout << (range | stringify(", "));
Was it helpful?

Solution

I don't believe it's ever been really finished/polished, but that's the intent of Boost.Explore.

Personally, I've just gotten used to using std::copy. For this sort of thing, an infix_ostream_iterator can be quite helpful at times. For example, something like this:

#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
#include "infix_iterator.h"

template <typename T>
std::ostream& operator<<(std::ostream &o, const std::vector<T>& v) { 
    o << "[";
    std::copy(v.begin(), v.end(), infix_ostream_iterator<T>(o, ", ")); 
    o << "]";
    return o; 
}

int main() { 

    std::vector<int> x;

    for (int i=0; i<20; i+=2)
        x.push_back(i);

    std::cout << x << "\n";
    return 0;
}

As it stands, this has operator<< taking a vector, but it would be relatively trivial to have it take a range instead.

OTHER TIPS

You might want to define a general function to output a forward iterable range. Here is join() function I used:

template<typename ForwardIter>
std::string join(const ForwardIter& it1, const ForwardIter& it2, const std::string& separator=" ") {
    if(it1 != it2) {
        std::ostringstream s;
        s << *it1;
        for(ForwardIter i=it1+1; i!=it2; i++)  s << separator << *i;
        return s.str();
    }
    else return "";
}

template<typename Range>
std::string join(const Range& r, const std::string& separator=" ") { return join(r.begin(), r.end(), separator); }

This function return a string and it should take any forward iterable range, such as list, vector, set, etc, in which the dereference of each element can be output individually by an ostream. With this function, you can use it like:

list<int> a = {1,2,3,4};
set<int> b = {4,3,2,1};
vector<int> c = {1,2,3,4};


cout << join(a) << endl;
cout << join(b, ",") << endl;
cout << join(c.begin(), c.begin()+2, "~") << endl;

I remember that the join() name might collide with some namespace, so you might want to change the name to similar name such as joining or joint. Also, you might want to use boost::begin(r) instead of r.begin() for better compatibility, if you ever use boost.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top