Copying with istream_iterator

A

Alex Vinokur

------ foo.cpp : BEGIN ------
#include <cassert>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;


int main ()
{
ifstream f("foo.in", ios_base::binary);
assert (f);
istream_iterator<string> b(f), e;

cout << "(1) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

cout << "(2) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

return 0;
}
------ foo.cpp : END --------


------ Compilation & Run : BEGIN ------

$ g++ --version
g++ (GCC) 3.3.1 (cygming special)
[---omitted---]


$ g++ -mno-cygwin foo.cpp

$ a
(1) File : abc xyz ijk pqr
(2) File : abc

------ Compilation & Run : END --------

Why do two copy()'s produce different output?
 
V

Victor Bazarov

Alex said:
------ foo.cpp : BEGIN ------
#include <cassert>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;


int main ()
{
ifstream f("foo.in", ios_base::binary);
assert (f);
istream_iterator<string> b(f), e;

cout << "(1) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

cout << "(2) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

return 0;
}
------ foo.cpp : END --------


------ Compilation & Run : BEGIN ------

$ g++ --version
g++ (GCC) 3.3.1 (cygming special)
[---omitted---]


$ g++ -mno-cygwin foo.cpp

$ a
(1) File : abc xyz ijk pqr
(2) File : abc

------ Compilation & Run : END --------

Why do two copy()'s produce different output?

Probably because you didn't rewind the stream. The 'istream_iterator'
holds a reference to the stream it's initialised with. Any attempt to
read past the end of the file produce undefined behaviour.

Victor
 
A

Alex Vinokur

Victor Bazarov said:
Alex Vinokur wrote: [snip]
Why do two copy()'s produce different output?

Probably because you didn't rewind the stream. The 'istream_iterator'
holds a reference to the stream it's initialised with. Any attempt to
read past the end of the file produce undefined behaviour.
[snip]


Something like:

------ foo1.cpp : BEGIN ------
#include <cassert>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;

int main ()
{
ifstream f("foo.in");
assert (f);
istream_iterator<string> b(f), e;

cout << distance (b, e) << endl;
f.seekg (0, ios::beg);
cout << distance (b, e) << endl;

return 0;
}
------ foo1.cpp : END --------

------ Run : BEGIN ------

$ a
4
1

------ Run : END --------

What happened to 'b' after first 'distance'?
Where should one put 'seekg' (rewind) to get the same result after second 'distance'?
 
V

Victor Bazarov

Alex said:
Victor Bazarov said:
Alex Vinokur wrote:
[snip]
Why do two copy()'s produce different output?

Probably because you didn't rewind the stream. The 'istream_iterator'
holds a reference to the stream it's initialised with. Any attempt to
read past the end of the file produce undefined behaviour.

[snip]


Something like:

------ foo1.cpp : BEGIN ------
#include <cassert>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;

int main ()
{
ifstream f("foo.in");
assert (f);
istream_iterator<string> b(f), e;

cout << distance (b, e) << endl;
f.seekg (0, ios::beg);
cout << distance (b, e) << endl;

return 0;
}
------ foo1.cpp : END --------

------ Run : BEGIN ------

$ a
4
1

------ Run : END --------

What happened to 'b' after first 'distance'?

'distance' uses operator++ to count the number of increments. That
changes the original iterator, doesn't it?
Where should one put 'seekg' (rewind) to get the same result after second 'distance'?

Nowhere. Don't use 'distance'. Perhaps it's a bug in your library
implementation. Did you check how 'distance' worked? The trouble
with 'istream_iterator' is that the connection between it and the
stream is mutating even after you make a copy of the iterator. You
might get lucky that your stream can be brought back to the same
state somehow, or you might never get lucky, and the stream, once it
has been read, can never go back to its beginning...

Why do you need to do that, anyway?

Victor
 
A

Alex Vinokur

Victor Bazarov said:
Alex said:
Victor Bazarov said:
Alex Vinokur wrote:
[snip]

Why do two copy()'s produce different output?

Probably because you didn't rewind the stream. The 'istream_iterator'
holds a reference to the stream it's initialised with. Any attempt to
read past the end of the file produce undefined behaviour.

[snip]


Something like:

------ foo1.cpp : BEGIN ------
#include <cassert>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;

int main ()
{
ifstream f("foo.in");
assert (f);
istream_iterator<string> b(f), e;

cout << distance (b, e) << endl;
f.seekg (0, ios::beg);
cout << distance (b, e) << endl;

return 0;
}
------ foo1.cpp : END --------

------ Run : BEGIN ------

$ a
4
1

------ Run : END --------

What happened to 'b' after first 'distance'?

'distance' uses operator++ to count the number of increments. That
changes the original iterator, doesn't it?
Where should one put 'seekg' (rewind) to get the same result after second 'distance'?

Nowhere. Don't use 'distance'. Perhaps it's a bug in your library
implementation.

Microsoft C++ Version 13.00.9466 for 80x86,
Borland C++ 5.5.1,
Digital Mars C++ 8.38n
produce the same behavior.
Did you check how 'distance' worked? The trouble
with 'istream_iterator' is that the connection between it and the
stream is mutating even after you make a copy of the iterator. You
might get lucky that your stream can be brought back to the same
state somehow, or you might never get lucky, and the stream, once it
has been read, can never go back to its beginning...

Why do you need to do that, anyway?
[snip]

I didn't need that, but I came across strange (to me) behavior of copy() while preparing my reply to thread
http://groups-beta.google.com/group/comp.lang.c++/browse_thread/thread/43ed64bb7a94fa3a/

Now I would like to understand this situation.
 
V

Victor Bazarov

Alex said:
Victor Bazarov said:
Alex said:
Alex Vinokur wrote:

[snip]


Why do two copy()'s produce different output?

Probably because you didn't rewind the stream. The 'istream_iterator'
holds a reference to the stream it's initialised with. Any attempt to
read past the end of the file produce undefined behaviour.

[snip]


Something like:

------ foo1.cpp : BEGIN ------
#include <cassert>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;

int main ()
{
ifstream f("foo.in");
assert (f);
istream_iterator<string> b(f), e;

cout << distance (b, e) << endl;
f.seekg (0, ios::beg);
cout << distance (b, e) << endl;

return 0;
}
------ foo1.cpp : END --------

------ Run : BEGIN ------

$ a
4
1

------ Run : END --------

What happened to 'b' after first 'distance'?

'distance' uses operator++ to count the number of increments. That
changes the original iterator, doesn't it?

Where should one put 'seekg' (rewind) to get the same result after second 'distance'?

Nowhere. Don't use 'distance'. Perhaps it's a bug in your library
implementation.


Microsoft C++ Version 13.00.9466 for 80x86,
Borland C++ 5.5.1,
Digital Mars C++ 8.38n
produce the same behavior.

Did you check how 'distance' worked? The trouble
with 'istream_iterator' is that the connection between it and the
stream is mutating even after you make a copy of the iterator. You
might get lucky that your stream can be brought back to the same
state somehow, or you might never get lucky, and the stream, once it
has been read, can never go back to its beginning...

Why do you need to do that, anyway?

[snip]

I didn't need that, but I came across strange (to me) behavior of copy() while preparing my reply to thread
http://groups-beta.google.com/group/comp.lang.c++/browse_thread/thread/43ed64bb7a94fa3a/

Now I would like to understand this situation.

If you feel like it, dig in the source of the C++ library (or libraries)
you're using. It seems that since 'copy' is a template, it probably gets
its arguments in the form of references (and not objects copied due to
passing by value). If it's so, the objects change while 'copy' does its
thing. How they change you can also discover by looking at the code. If
you find it interesting/revealing/puzzling, do post again.

You could also experiment with passing by value:

...
void foo(istream_iterator b, istream_iterator e)
{
std::copy(b, e, ...
}

int main()
{
...
foo(b, e);
// reset the stream
std::copy(b, e, ...
}

and see if it makes any difference...

V
 
T

tom_usenet

int main ()
{
ifstream f("foo.in", ios_base::binary);
assert (f);
istream_iterator<string> b(f), e;

cout << "(1) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

cout << "(2) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

return 0;
}
(1) File : abc xyz ijk pqr
(2) File : abc
Why do two copy()'s produce different output?

When you take a copy of an input iterator, the original one is still
affected by changes to the copy. istream_iterator can only be used for
1-pass algorithms. Rewinding the stream and then constructing a new
iterator from the stream is the only safe way to do a second pass.
e.g. something like:


int main ()
{
ifstream f("foo.in", ios_base::binary);
assert (f);
istream_iterator<string> b(f), e;

cout << "(1) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

f.seekg(0, std::ios_base::beg);
b = istream_iterator<string>(f);

cout << "(2) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

return 0;
}

Tom
 
A

Alex Vinokur

tom_usenet said:
When you take a copy of an input iterator, the original one is still
affected by changes to the copy. istream_iterator can only be used for
1-pass algorithms. Rewinding the stream and then constructing a new
iterator from the stream is the only safe way to do a second pass.
e.g. something like:


int main ()
{
ifstream f("foo.in", ios_base::binary);
assert (f);
istream_iterator<string> b(f), e;

cout << "(1) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

f.seekg(0, std::ios_base::beg);
b = istream_iterator<string>(f);

It doesn't help.
cout << "(2) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

return 0;
}

Tom

Here is some program which demonstrates behavior of istream_iterator.

====== File foo.cpp : BEGIN ======
#include <cassert>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;

int main ()
{
ifstream f("foo.in", ios::binary);
assert (f);

cout << "Before istream_iterator : tellg() = " << f.tellg() << endl;
cout << endl;

istream_iterator<string> b(f), e;
cout << "--- First ---" << endl;
cout << "After istream_iterator : tellg() = " << f.tellg() << endl;


cout << "Output-1.1 = ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;
cout << "After first copy : tellg() = " << f.tellg() << endl;
cout << "Output-1.2 = ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;
cout << endl;


cout << "--- Second ---" << endl;
f.clear();
f.seekg(0, ios::beg);
cout << "After clear and seekg : tellg() = " << f.tellg() << endl;


cout << "Output-2.1 = ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;
cout << "After second copy : tellg() = " << f.tellg() << endl;
cout << endl;


cout << "--- Third ---" << endl;
f.clear();
f.seekg(0, ios::beg);
cout << "After clear and seekg : tellg() = " << f.tellg() << endl;
b++;
cout << "After b++ : tellg() = " << f.tellg() << endl;


cout << "Output-3.1 = ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;
cout << "After third copy : tellg() = " << f.tellg() << endl;
cout << endl;


return 0;
}
====== File foo.cpp : END ========


====== Compilation & Run : BEGIN ======

$ g++ --version
g++ (GCC) 3.3.1 (cygming special)
[---omitted---]

$ g++ foo.cpp

$ a

Before istream_iterator : tellg() = 0

--- First ---
After istream_iterator : tellg() = 3
Output-1.1 = aaa bbb ccc ddd
After first copy : tellg() = -1
Output-1.2 = aaa

--- Second ---
After clear and seekg : tellg() = 0
Output-2.1 = aaa aaa bbb ccc ddd
After second copy : tellg() = -1

--- Third ---
After clear and seekg : tellg() = 0
After b++ : tellg() = 3
Output-3.1 = aaa bbb ccc ddd
After third copy : tellg() = -1

====== Compilation & Run : END ========
 
V

Victor Bazarov

Alex said:
It doesn't help.

You probably also need to do

f.clear();

before f.seekg( ..

Once the EOF is reached, the stream becomes non-responsive until
you clear its error state.

V
cout << "(2) File : ";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;

return 0;
}

Tom

[...]
 
A

Alex Vinokur

Victor Bazarov said:
If you feel like it, dig in the source of the C++ library (or libraries)
you're using. It seems that since 'copy' is a template, it probably gets
its arguments in the form of references (and not objects copied due to
passing by value). If it's so, the objects change while 'copy' does its
thing. How they change you can also discover by looking at the code. If
you find it interesting/revealing/puzzling, do post again.

You could also experiment with passing by value:

...
void foo(istream_iterator b, istream_iterator e)
{
std::copy(b, e, ...
}

int main()
{
...
foo(b, e);
// reset the stream
std::copy(b, e, ...
}

and see if it makes any difference...

V


We can see that there is no difference between passing by value and passing by reference because
a position of the pointer in istream 'f' (not istream_iterator 'b') determines such behavior.

====== foo.cpp : BEGIN ======
#include <cassert>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
using namespace std;

void copy_by_value(istream_iterator<string> b, istream_iterator<string> e)
{
copy (b, e, ostream_iterator<string> (cout, " "));
}

void copy_by_ref(const istream_iterator<string>& b, const istream_iterator<string>& e)
{
copy (b, e, ostream_iterator<string> (cout, " "));
}

void test_by_value()
{
cout << "=== test_by_value ===" << endl;
ifstream f("foo.in", ios_base::binary);
assert (f);
cout << "Before istream_iterator : tellg() = " << f.tellg() << endl;
istream_iterator<string> b(f), e;
cout << "After istream_iterator : tellg() = " << f.tellg() << endl;
cout << "Output-1 =";
copy_by_value (b, e);
cout << endl;
cout << "After copy_by_value : tellg() = " << f.tellg() << endl;

cout << "Output-2 =";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;
cout << endl;
}

void test_by_ref()
{
cout << "=== test_by_ref ===" << endl;
ifstream f("foo.in", ios_base::binary);
assert (f);
cout << "Before istream_iterator : tellg() = " << f.tellg() << endl;
istream_iterator<string> b(f), e;
cout << "After istream_iterator : tellg() = " << f.tellg() << endl;
cout << "Output-1 =";
copy_by_ref (b, e);
cout << endl;
cout << "After copy_by_ref : tellg() = " << f.tellg() << endl;

cout << "Output-2 =";
copy (b, e, ostream_iterator<string> (cout, " "));
cout << endl;
cout << endl;
}


int main ()
{
test_by_value();
test_by_ref();

return 0;
}
====== foo.cpp : END ========


###### Run : BEGIN ######

=== test_by_value ===
Before istream_iterator : tellg() = 0
After istream_iterator : tellg() = 3
Output-1 =aaa bbb ccc ddd
After copy_by_value : tellg() = -1
Output-2 =aaa

=== test_by_ref ===
Before istream_iterator : tellg() = 0
After istream_iterator : tellg() = 3
Output-1 =aaa bbb ccc ddd
After copy_by_ref : tellg() = -1
Output-2 =aaa


###### Run : END ######
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top