ostream_iterator and the delimiter before the item

S

ShaunJ

I often need to output a list with a delimiter (such as a tab or a
comma) before the item rather than after (which is where the end-of-
line will go). ostream_iterator puts the delimiter after the item. Is
there a simple solution for creating an ostream_iterator that puts the
delimiter before the item?

If I do it in a for loop, it's easy, of course. But the function I'm
calling takes an output iterator parameter, and to use a for loop I
would have to store all the results in a list first, which would take
a bunch of memory and is totally unnecessary.

Thanks,
Shaun
 
A

Alf P. Steinbach

* ShaunJ:
I often need to output a list with a delimiter (such as a tab or a
comma) before the item rather than after (which is where the end-of-
line will go). ostream_iterator puts the delimiter after the item. Is
there a simple solution for creating an ostream_iterator that puts the
delimiter before the item?

If I do it in a for loop, it's easy, of course. But the function I'm
calling takes an output iterator parameter, and to use a for loop I
would have to store all the results in a list first, which would take
a bunch of memory and is totally unnecessary.

class TabbedIntIter
{
private:
std::eek:stream* pMyStream;
public:
typedef char char_type;
typedef std::eek:stream ostream_type;
typedef std::eek:stream::traits_type traits_type;

TabbedIntIter( std::eek:stream& stream )
: pMyStream( &stream )
{}

TabbedIntIter& operator*() { return *this; }
TabbedIntIter& operator++() { return *this; }

TabbedIntIter& operator=( int x )
{
(*pMyStream) << '\t' << x;
return *this;
}
};

int main()
{
foo( TabbedIntIter( std::cout ) );
}


Cheers & hth.,

- Alf
 
J

Jerry Coffin

I often need to output a list with a delimiter (such as a tab or a
comma) before the item rather than after (which is where the end-of-
line will go). ostream_iterator puts the delimiter after the item. Is
there a simple solution for creating an ostream_iterator that puts the
delimiter before the item?

Fairly simple to implement, if somewhat more verbose than anybody would
like:

// Warning: only minimally tested.
// prefix_iterator.h
#include <ostream>
#include <iterator>

template <class T,
class charT=char,
class prefix_ostream_iterator :
public std::iterator<std::eek:utput_iterator_tag,void,void,void,void>
{
charT const* delimiter;
std::basic_ostream<charT,traits> *os;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT,traits> ostream_type;

prefix_ostream_iterator(ostream_type& s)
: os(&s),delimiter(0)
{}
prefix_ostream_iterator(ostream_type& s, charT const *d)
: os(&s),delimiter(d)
{}
prefix_ostream_iterator<T,charT,traits>& operator=(T const &item)
{
// Here's the only real change from ostream_iterator:
// Normally, the '*os << item;' would come before the 'if'.
if (delimiter != 0)
*os << delimiter;
*os << item;
return *this;
}

prefix_ostream_iterator<T,charT,traits> &operator*() {
return *this;
}
prefix_ostream_iterator<T,charT,traits> &operator++() {
return *this;
}
prefix_ostream_iterator<T,charT,traits> &operator++(int) {
return *this;
}
};

// Minimal test file:
#include <vector>
#include <iostream>
#include "prefix_iterator.h"

int main() {
std::vector<int> x;

x.push_back(1);
x.push_back(3);
x.push_back(5);

std::copy(x.begin(), x.end(),
prefix_ostream_iterator<int>(std::cout, "|"));
return 0;
}

The test code is pretty minimal, but more probably isn't necessary --
this is little more than a trivial modification of the code as it's
presented in the standard, and most of it is pretty basic boilerplate.
 
R

red floyd

Jerry,

I took the liberty of modifying your prefix_output_iterator to create
the much
desired "infix iterator", would generate:

1|3|5

from your test case.

// Warning: only minimally tested.
// infix_iterator.h
// Lifted from Jerry Coffin's 's prefix_ostream_iterator
#if !defined(INFIX_ITERATOR_H_)
#define INFIX_ITERATOR_H_
#include <ostream>
#include <iterator>

template <class T,
class charT=char,
class traits=std::char_traits<charT> >

class infix_ostream_iterator :
public std::iterator<std::eek:utput_iterator_tag,void,void,void,void>
{
std::basic_ostream<charT,traits> *os;
charT const* delimiter;
bool first_elem;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT,traits> ostream_type;

infix_ostream_iterator(ostream_type& s)
: os(&s),delimiter(0), first_elem(true)
{}
infix_ostream_iterator(ostream_type& s, charT const *d)
: os(&s),delimiter(d), first_elem(true)
{}
infix_ostream_iterator<T,charT,traits>& operator=(T const &item)
{
// Here's the only real change from ostream_iterator:
// Normally, the '*os << item;' would come before the 'if'.
if (!first_elem && delimiter != 0)
*os << delimiter;
*os << item;
first_elem = false;
return *this;
}

infix_ostream_iterator<T,charT,traits> &operator*() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++(int) {
return *this;
}

};
#endif
 
J

Jerry Coffin

Jerry,

I took the liberty of modifying your prefix_output_iterator to create
the much desired "infix iterator", would generate:

1|3|5

from your test case.

Good call. I'm not quite sure why a suffix was chosen for the standard
-- I think it's probably desired the least often of the three obvious
possibilities.
 
T

Thomas J. Gritzan

Jerry said:
Good call. I'm not quite sure why a suffix was chosen for the standard
-- I think it's probably desired the least often of the three obvious
possibilities.

Because it works great when you simply want a newline after each item.
 
J

James Kanze

Good call. I'm not quite sure why a suffix was chosen for the
standard -- I think it's probably desired the least often of
the three obvious possibilities.

I've never needed the prefix, and of the latter two, infix is
actually tricky if the iterators are getting copied. (In
practice, however, using a copy of an output iterator once
anything has been written through it is probably going to cause
problems anyway, so Red Floyd's solution should work. But I
think I'd still have used a shared_ptr to a dynamically
allocated bool for "first_element".)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top