1

I have a following container which I would like to store in a file:

std::vector< std::vector< Point > > m_vPoints;

Point is a basic structure with defined operator<<() and operator>>().

The format of the file is:

point0_0 point0_1 point0_2 ... <-- points from m_vPoints[0]
point1_0 point1_1 ...          <-- points from m_vPoints[1]
...

point elements are separated by ',', and points are separated by ' ' e.g.:

-5,4 6,12 -7,32 ...
12,0 -3,4 ...

I managed to produce such a file with:

std::ostream& operator<<(std::ostream &o, ...)
{
    for(auto it=m_vPoints.begin(); it!=m_vPoints.end(); ++it)
    {
        copy(it->begin(), it->end(), std::ostream_iterator<Point>(o," "));
        o << endl;
    }
    return o;
}

and it works fine. The problem is however with reading. When I try the following:

std::istream& operator>>(std::istream &is, ...)
{
    int numberOfRows; // assume it is known and valid
    m_vPoints.resize(numberOfRows);
    for(int i=0; i<numberOfRows; i++)
    {
        copy(std::istream_iterator<Point>(is), std::istream_iterator<Point>(), std::back_inserter(m_vPoints[i]));
    }
    return is;
}

all points from all lines are read into m_vPoints[0]. It looks like std::istream_iterator<Point>() ignores std::endl. Using is >> noskipws will not work, because I still want to skip whitespaces that separate individual points. So basically, the problem is to copy from the istream but not until end-of-stream is reached but until end-of-line. Examples I saw in the net using cin somehow manage to parse a single line, ignoring space characters, but correctly ending at std::endl or '\n'

I'd also like to avoid getline(), as then I'd have to copy each line to a string and then parse the string. Instead, I'd like to copy line i directly to container m_vPoints[i]

Piotr G
  • 959
  • 1
  • 7
  • 25

1 Answers1

5

Read line by line:

std::istream& operator>>(std::istream &is,
                         std::vector<std::vector<Point>>& points)
{
    using namespace std;
    using point_itr = istream_iterator<Point>;
    string line;
    while( getline(is, line) ) // read line by line
    {
        // iterate points from istringstream into new line vector
        istringstream line_in{ line };
        points.emplace_back(
            make_move_iterator(point_itr{ line_in }),
            make_move_iterator(point_itr{}) );
    }
    return is;
}

Edit:

This is the canonical solution.

I'd also like to avoid getline(), as then I'd have to copy each line to a string and then parse the string. Instead, I'd like to copy line i directly to container m_vPoints[i]

Is there a reason you want to avoid std::getline?

You could use if(is.peek() == '\n') in your code, after reading each point. That would make your code (more) complicated as you would no longer be able to copy from iterators directly, and the actual code to do it would be more complex (you would also have to process input stream error states manually).

utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • I wanted to avoid getline so to avoid copying from `string` to `points`, but your solution seems to deal with this issue. One thing is I can't compile the solution you proposed, because: `cannot convert from 'const DisparityPixel' to 'DisparityPixel &&' ` at `std::make_move_iterator(point_itr(line_in)),` Also, I replaced {} with () as VS2012 seemed to dislike it. – Piotr G Aug 12 '15 at 12:41
  • To fix compilation issue (I assumed the points were movable) define `DisparityPixel::DisparityPixel(DisparityPixel&&)` constructor. If you cannot do that, simply remove the `make_move_iterator` call. I had the same issue with VStudio 2013 initializer list support. – utnapistim Aug 12 '15 at 12:48
  • I created move constructor, but it didn't help - I suppose it has sth. to do with `const` qualifier. Additional part of the error was: `while compiling class template member function 'DisparityPixel &&std::move_iterator<_RanIt>::operator *(void) const'`. Not sure why, because I am not using `const` anywhere in the definition of `DisparityPixel`. Of course, class `DisparityPixel` corresponds to previously introduced `Point`. – Piotr G Aug 12 '15 at 13:17
  • Did you also add a move assignment? (I should have mentioned it but didn't, because normally when you define the move constructor you define the move assignment operator as well). – utnapistim Aug 12 '15 at 13:32
  • Yes. I also get rid of make_move_iterator and used just istream_iterator to initialize vector, but sth. is still not working. – Piotr G Aug 12 '15 at 14:05
  • can you post the declaration of the `DisparityPixel` class? – utnapistim Aug 12 '15 at 20:13
  • I think it has nothing to do with class DisparityPixel. I replaced `DisparityPixel` with `char` and reduced the code to the following: `typedef istream_iterator disppix_itr ; disppix_itr eos; string ll("some text here"); istringstream line_in(ll); disppix_itr start(line_in); move_iterator mstart(start); move_iterator meos(eos); vector vvc(mstart, meos); ` Using move_iterators will not work, using copy iterators will work – Piotr G Aug 13 '15 at 08:51
  • 1
    It turns out, one can't make move_iterators from istream_iterator directly, see: http://stackoverflow.com/questions/31985373/can-one-make-move-iterator-from-istream-iterator . Anyway, thanks for helping out. – Piotr G Aug 13 '15 at 12:53