Thread: istream_iterator

  1. #1
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663

    istream_iterator

    Hi,

    I'm trying to get some example code to work, but I'm getting two errors:

    error C2065: 'istream_interator' : undeclared identifier
    error C2062: type 'int' unexpected

    Code:
    #include <iostream>
    #include <vector>
    #include <iterator>
    
    using namespace std;
    
    template<typename Iter>
    void display(Iter start, Iter end)
    {
    	while(start != end)
    	{
    		cout<<*start++<<endl;
    	}
    }
    
    int main()
    {
    	
    	int nums[] = {1, 2, 3};
    	display(nums, nums + 3);
    	
    	cout<<"enter some ints:"<<endl;
    	display(  istream_iterator<int>(cin), istream_interator<int>()  ); //ERROR
    
    	return 0;
    }

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> error C2065: 'istream_interator' : undeclared identifier
    What are you interating over?

    gg

  3. #3
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    cin?

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Typo: istream_interator



    Sounds like a way to dispose of naughty integral values.

  5. #5
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    Sheesh! I checked the spelling a couple of times, and even compared the spelling of each 'iterator' against the other one, and I still missed it.

    Thanks.

  6. #6
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    Another problem:

    If I run the program and type in some numbers:

    enter some ints:
    4 5 6
    then hit Ctrl/Z, this is the output:

    enter some ints:
    4 5 65
    6
    If I add one endl:
    Code:
    template<typename Iter>
    void display(Iter start, Iter end)
    {
    	cout<<endl;
    	while(start != end)
    	{
    		cout<<*start++<<endl;
    	}
    }
    I get this:
    enter some ints:
    4 5 64
    5
    6
    and with two endl's:
    Code:
    template<typename Iter>
    void display(Iter start, Iter end)
    {
    	cout<<endl<<endl;
    	while(start != end)
    	{
    		cout<<*start++<<endl;
    	}
    }
    I get this:
    enter some ints:
    4 5 6
    4
    5
    6
    Can someone explain that? I expect it has something to do with the way the cin buffer is flushed.
    Last edited by 7stud; 04-29-2005 at 12:58 AM.

  7. #7
    Registered User Micko's Avatar
    Join Date
    Nov 2003
    Posts
    715
    Recently I had similar problem with weird behavior. I also used something like this:
    Code:
    istream_iterator <int> it(cin);
    istream_iterator <int> end;
    while(it != end)
    {
        cout<<*it++;
    }
    If I enter: 1 2 3 ^Z program will display
    12
    and waits only after another ^Z it will exit.
    I don't know why. Answer probably has something to do with the way how istream_iterator <int>() works. Maybe this way of using while loop and != operator is not very good idea?
    I found this explanation
    "The expression istream_iterator<string>()
    calls the default constructor of istream iterators that creates an end-of-stream iterator. It represents a stream from which you can no longer read"

    So when input sequence is 1 2 3 ^Z, ^Z will not cause creation of end-of-stream iterator an yet another ^Z will do the trick. Can someone please explain what is going on here.

    P.S. 7stud, since we have common problem I decide to post this in your thread. Hope you don't mind....

    Thank you.

    - Micko
    Gotta love the "please fix this for me, but I'm not going to tell you which functions we're allowed to use" posts.
    It's like teaching people to walk by first breaking their legs - muppet teachers! - Salem

  8. #8
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    P.S. 7stud, since we have common problem I decide to post this in your thread. Hope you don't mind....
    Of course not.

    I found this explanation
    "The expression istream_iterator<string>()
    calls the default constructor of istream iterators that creates an end-of-stream iterator. It represents a stream from which you can no longer read"
    So when input sequence is 1 2 3 ^Z, ^Z will not cause creation of end-of-stream iterator
    The way I understand it, this line:

    istream_iterator <int> end;

    calls the default constructror for istream_iterator<int>. Presumably, the template for istream_iterator<int> was created by the call:

    istream_iterator <int> it(cin);

    So, the line

    istream_iterator <int> end;

    uses the default constructor in that class. After that, my book says end will behave like a value that corresponds to end-of-file. So, ^Z doesn't create an iterator--instead it should be equivalent to the end-of-file value that is stored in end.

    That doesn't mean I can explain the output though.
    Last edited by 7stud; 04-30-2005 at 05:45 AM.

  9. #9
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    This is an interesting problem in that there are issues with: the code, the MS CRT implementation, and the MS STL implementation.

    First, the code. istream_iterator<X> performs an operator>>(X) on the input stream when the iterator is constructed and when the iterator is incremented. When istream_iterator is default constructed, it represents an iterator of an input stream which is no longer good (ie. istream.good() == false). That means that a default constructed istream_iterator should only be used for comparisons. So far nothing new...

    The issue with the display() code stems from the "*start++" construct. When input of "1 2 3<enter>" is used, you should see "1<endl>2<endl>". So what happened to 3? The answer is that cin is waiting for the next integer input (after 3), but before 3 is displayed.
    Why?
    Well, to understand why, let's rewrite "*start++" to logically equivalent code:
    Code:
       [1] Iter tmp = start;
       [2] start++;
       [3] cout << *tmp << endl;
    It should be easier to why 3 is not displayed. First, istream_iterator<int>(cin) is constructed which initiates the first "cin >> int" operation. Remember that input is buffered, so were actually inside the istream_iterator constructor when we type "1 2 3<return>". Hitting <return> causes execution to return from "cin >> int" after the 1 is extracted from the stream. Then we go into display(). Since cin is still good, the expression "start != end" is true. Then on line [1], we create a copy of the iterator witch initially contains 1. Line [2] then extracts the next integer from the stream, which is readily available. Then "1<endl>" is sent to cout on line [3].
    Eventually, line [2] will execute and there won't be any buffered input to extract integers from, so the call "blocks" waiting for input from the keyboard, but before the previously extracted integer is displayed.

    Now on to the MS implementation issues. The first "issue" is within the MS CRT implementation in how it handles CTRL-Z on stdin (when stdin is not redirected). What it does is this: If the first character read is CTRL-Z, then EOF is returned. If the first character read is not CTRL-Z, the buffered input from stdin is truncated at the first occurrence of CTRL-Z.
    To demonstrate this issue, I'll use an istreambuf_iterator<char>(cin) to get the raw characters from stdin:
    Code:
    #include <iostream>
    #include <iterator>
    using namespace std;
    
    template<typename Iter>
    void display(Iter start, Iter end)
    {
        while (start != end)
        {
            // display the contents of "*start" as an int
            cout << "[" << int(*start) << "]" << endl;
            start++;
        }//while
    
        // display cin's state bits
        // NOTE: when using an istreambuf_iterator<cin>, the state will not change
        //       since it operates on the stream's buffer
        ios_base::iostate st = cin.rdstate();
    
        cout << "cin.rdstate() == ";
        if (st & ios_base::eofbit)
            cout << "eofbit | ";
        if (st & ios_base::failbit)
            cout << "failbit | ";
        if (st & ios_base::badbit)
            cout << "badbit | ";
        if (st == ios_base::goodbit)
            cout << "goodbit | ";
        
        cout << "\b\b " << endl; // remove the trailing "| "
    }//display
    
    int main()
    {
        display(istreambuf_iterator<char>(cin), istreambuf_iterator<char>());
    
        return 0;
    }//main
    The following test run shows input in blue:
    Code:
    ab<enter>
    [97]
    [98]
    [10]
    a^Zb<enter>
    [97]
    ^Z<enter>
    cin.rdstate() == goodbit
    As you can see, placing a CTRL-Z between the 'a' and 'b' caused the 'b' and the <return> to be ignored. Using CTRL-Z as the first character of the line caused the streambuf to reach the "end-of-file". Those of you using MinGW will find the same behavior because it uses the MS CRT under the hood.

    Now on to the issue with the MS STL implementation. Here I'm referring to the MS STL that ships with VC++ 6.0. The issue here is that they "broke" CTRL-Z even further when using a formatted-input function on stdin. That is, CTRL-Z as the first character of a line doesn't work as expected. To demonstrate this one, we'll perform the same test as above, except we'll use an istream_iterator<char> in the call to display().
    Code:
    ab<enter>
    [97]
    [98]
    a^Zb<enter>
    [97]
    ^Z<enter>
    <enter>
    [10]
    ^Z<enter>
    ^Z<enter>
    cin.rdstate() == eofbit | failbit
    Why you have to hit CTRL-Z <enter> twice to generate an "end-of-file" is beyond me. The GNU STL implementation that comes with Dev-C++ 4.9.8 handles CTRL-Z as expected, only if it's the first thing read from unbuffered input (but that's not their fault, unless you blame them for using the MS CRT ).

    I would be interested in other's results using different implementations (STLport or 7.0 MS CRT).

    gg

  10. #10
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    When input of "1 2 3<enter>" is used, you should see "1<endl>2<endl>". So what happened to 3?
    I'm not sure how that's relevant since we are using ^Z not a newline to end the input, but thanks for the detailed explanation.

    Using CTRL-Z as the first character of the line caused the streambuf to reach the "end-of-file".
    So, are you saying there's no cin stream error message because you're using an iterator that doesn't affect cin?

    Now on to the issue with the MS STL implementation.
    Why doesn't the istream_iterator display the newline as the istreambuf_iterator did?
    Code:
    ab<enter>  
    [97]             ---> [97]
    [98]             ---> [98]
    [10]             ---> nothing
    Last edited by 7stud; 05-02-2005 at 09:21 AM.

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> I'm not sure how that's relevant...
    It's relevant because using a construct of "*start++" doesn't do what many would expect it to do when iterating over the standard input stream cin.
    Or more specifically:
    Quote Originally Posted by Micko
    If I enter: 1 2 3 ^Z program will display
    12
    ...
    P.S. 7stud, since we have common problem I decide to post this in your thread. Hope you don't mind....
    Even though the 3 not showing wasn't a stated concern, I just wanted to clarify why.

    >> ...there's no cin stream error message because you're using an [streambuf] iterator that doesn't affect cin?
    Correct. istreambuf_iterator<char>(cin) operates directly on cin's stream buffer and does not affect cin's state bits.
    Since this is the C++ board, I used an istreambuf_iterator to demonstrate the Ctrl-Z issues that exist with getchar()/getc(stdin) in the MS CRT. It also shows that the MS CRT at least handles Ctrl-Z as "end-of-file" when Ctrl-Z is the first thing read. This evidence was also needed to show that the MS STL implementation further botched Ctrl-Z handling under formatted input.
    I was going to post some code that looped over getchar() but I thought an istreambuf_iterator was cooler.

    >> Why doesn't the istream_iterator display the newline as the istreambuf_iterator did?
    Because istream_iterator uses formatted input via operator>>(). If you want to see white space characters in your formatted input, add the following line before the call to display():
    Code:
    cin.unsetf(ios_base::skipws);
    gg
    Last edited by Codeplug; 05-02-2005 at 10:22 AM.

Popular pages Recent additions subscribe to a feed