Questions on custom stream manipulators \ cin.clear()

This is a discussion on Questions on custom stream manipulators \ cin.clear() within the C++ Programming forums, part of the General Programming Boards category; Here's a custom stream manipulator straight from my C++ book : Code: #include <iostream> using std::cout; using std::endl; using std::ostream; ...

  1. #1
    former member Brain Cell's Avatar
    Join Date
    Feb 2004
    Posts
    472

    Post Questions on custom stream manipulators \ cin.clear()

    Here's a custom stream manipulator straight from my C++ book :

    Code:
    #include <iostream>
    using std::cout;
    using std::endl;
    using std::ostream;
    using std::istream;
    using std::flush;
    
    ostream &endLine(ostream &output)
    {
    	return output << '\n' << flush;
    }
    
    int main(void)
    {
    	cout << endLine << "hello" << endLine;
    
    	return 0;
    }
    I didn't really get this. Its not working like i learned in operator overloading so i'm confused a bit. My questions are :

    1. How did endLine send a reference to osteram implicitly? do functions within cout take their left operand as their argument? or is 'ostream' assumed when endLine appeared within cout?

    2. I've read a few times that buffers are flushed either when the user force it (with flush) or when they recieve a newline , is this true? if so , then why would we need 'flush' after the '\n' here?

    3. how does return work in endLine? does it execute from right to left then finally return a reference to ostream for examaple?


    cin.clear()'s question :
    Why does this program keeps looping after reseting the error state with cin.clear()?
    Code:
    int x;
    while(1)
    { 
    cin >> x;  // enter a character here to change error flags
    cin.clear()  // the program would loop without taking inputs even
                    // though i reseted the flags
    }
    appreciate your help
    My Tutorials :
    - Bad programming practices in : C
    - C\C++ Tips
    (constrcutive criticism is very welcome)


    - Brain Cell

  2. #2
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,799
    Quote Originally Posted by Brain Cell
    1. How did endLine send a reference to osteram implicitly? do functions within cout take their left operand as their argument? or is 'ostream' assumed when endLine appeared within cout?
    From Nicolai Josuttis "The Standard C++ Library" 13.6.1 How Manipulators Work (page 612)

    Manipulators are implemented using a very simple trick. This trick not only enables the convenient manipulation of streams, it also demonstrates the power provided by function overloading. Manipulators are nothing more than functions that are passed to the I/O operators as arguments. The functions are then called by the operator. For example, the output operator for class ostream is basically overloaded like this:

    Code:
    ostream& ostream::operator << ( ostream& (*op)(ostream&))
    {
        // call the function passed as parameter with this stream as the argument
        return (*op)(*this);
    }
    The argument op is a pointer to a function. More precisely, it is a function that takes ostream as an argument and returns ostream (it is assumed that the ostream given as the argument is returned). If the second operand of operator << is such a function, this function is called with the first operand of operator << as the argument.

    This may sound very complicated, but it is actually relatively simple. An example should make it clearer. The manipulator (that is, the function) endl for ostream is implemented basically like this:
    Code:
    std::ostream& std::endl (std::ostream& strm)
    {
        // write newline
        strm.put('\n');
    
        // flush the output buffer
        strm.flush();
    
        // return strm to allow chaining
        return strm;
    }
    You can use this manipulator in an expression such as the following:
    Code:
    std::cout << std::endl;
    Here operator << is called for stream cout with the endl() function as the second operand. The implementation of operator << transforms this call into a call of the passed function with the stream as the argument.
    Code:
    std::endl(std::cout);
    Quote Originally Posted by Brain Cell
    2. I've read a few times that buffers are flushed either when the user force it (with flush) or when they recieve a newline , is this true? if so , then why would we need 'flush' after the '\n' here?
    In buffered output, the buffer is flushed whenever it gets full or when the user specifies by calling the flush function on the stream in question. Simply writing a newline character by itself does not flush the buffer. Calling endl does have the effect of writting a newline and flushing the buffer since that is how it is implemented (just like your endLine function does).

    Quote Originally Posted by Brain Cell
    3. how does return work in endLine? does it execute from right to left then finally return a reference to ostream for examaple?
    It returns the modified output stream back working left to right. If this was not done, you could not do chaining, i.e.:

    Code:
    cout << endLine << "blah";  // Would not work if endLine did not return the stream back
    In the above code we can look at it as two seperate << calls:

    Code:
    cout << endLine << "blah";  // cout << endLine returns cout
      \        /    /    /
       \      /    /    /
        \    /    /    /
         cout    << "blah";  // This cout here is what was returned by the call to endLine above
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  3. #3
    former member Brain Cell's Avatar
    Join Date
    Feb 2004
    Posts
    472
    Thanks for replying hk_mp5kpdw , i still don't get it clearly though...

    I didn't get your answer for the first question. endLine isn't part of a class and it doesn't use operator overloading , so how did it take ostream as an argument?

    about the return statement , is it some kind of recursion? i thought its done this way :

    step 1 : return output << "\n" << flush;

    step 2 : return output << flush;

    step 3 : return output;

    did i get it right?


    and there's still the cin.clear() question for whoever can answer it
    My Tutorials :
    - Bad programming practices in : C
    - C\C++ Tips
    (constrcutive criticism is very welcome)


    - Brain Cell

  4. #4
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,799
    Quote Originally Posted by Brain Cell
    Thanks for replying hk_mp5kpdw , i still don't get it clearly though...

    I didn't get your answer for the first question. endLine isn't part of a class and it doesn't use operator overloading , so how did it take ostream as an argument?
    You're right, endLine isn't part of a class. What endLine is happens to be a function that accepts an ostream object and then returns that ostream object. There are many different overloaded stream insertion calls dependent on the type of data being inserted into the output stream. We have ones for int, char, float, etc... We also have an overloaded insertion call that accepts functions taking ostream objects as parameters and returning a reference to an ostream object (which is what the endLine function is) as exemplified by the following (as per my first post):

    Code:
    ostream& ostream::operator << ( ostream& (*op)(ostream&))
    {
        // call the function passed as parameter with this stream as the argument
        return (*op)(*this);
    }
    In this case, since the endLine function matches exactly the type of the argument for the above overloaded version of the insertion operator (a function accepting an ostream object and returning an ostream object), this is the version of the operator that gets picked to be called when your endLine function is inserted onto the stream. op in this instance, which is a function pointer variable (it may help to study up of function pointers at this juncture perhaps), would get replaced with the address of the endLine function. Now, if we had done something like cout << endLine;, then when we get to the (*op)(*this) call in the above overloaded insertion operator, op is equal to endLine and *this is equal to cout in our case so that line ends up looking like this:

    Code:
    return endLine(cout);
    Now, since endLine returns the cout stream back in this case, it gets returned by the << operator. This means that in your original post where you had:
    Code:
    cout << endLine << "hello" << endLine;
    The first insertion of the endLine function into cout has the affect of calling endLine function with cout as a parameter and returning cout back. So after that first endLine insertion is done with, we could look at the original line of code above and consider it to now be altered as follows:

    Code:
    cout << "hello" << endLine;
    The chaining continues on with the other two arguments "hello" followed by a second call to the endLine function.

    Don't know if I can make it any more simple than that. I'm sure I might potentially be making things worse with my explanation.

    Quote Originally Posted by Brain Cell
    about the return statement , is it some kind of recursion? i thought its done this way :

    step 1 : return output << "\n" << flush;

    step 2 : return output << flush;

    step 3 : return output;

    did i get it right?
    Essentially, your steps do illustrate what is happening. The newline character gets pushed onto the stream and the stream itself is returned which gets us to step 2. Then the flush function gets called which is a function which accepts an ostream object as a parameter and returns that stream back and gets us to step 3. I'm am unsure myself whether I would call it recursion or not. It does seem like a version of it in some regards.

    Quote Originally Posted by Brain Cell
    and there's still the cin.clear() question for whoever can answer it
    Well, as written, the main reason it keeps looping is because of the while(1). I suspect that isn't quite what you were trying to get at however. Try running this instead and entering a character instead of the integer that it asks for and see what you get:

    Code:
    int iVal;
    char cVal;
    int loop = 0;
    while(1)
    {
        ++loop;
        std::cout << "Enter an integer: ";
        if( !(std::cin >> iVal) )
        {
            std::cin.clear();
            std::cout << std::endl;
        }
        if( loop == 10 )
        {
            std::cin.clear();
            std::cin >> cVal;
            if( std::cin.good() ) break;
        }
    }
    std::cout << "cVal is: " << cVal << std::endl;
    The code asks for an integer and assuming you enter a character it seems to continuously loop without the user being able to enter in anymore input even though we are clearing the error flags. This is the behavior I think you were getting at right? I have the code above finally processing the character input after 10 loops and exiting the loop if the extraction is a success (did this to save on my sanity). My output:

    Code:
    Enter an integer: r
    
    Enter an integer:
    Enter an integer:
    Enter an integer:
    Enter an integer:
    Enter an integer:
    Enter an integer:
    Enter an integer:
    Enter an integer:
    Enter an integer:
    cVal is: r
    The program when run only stops to ask for input once. The reason you still can't enter any input after clearing the error flags is that the bad value is still in the stream. In my case above the 'r' character never gets extracted and the next time through the loop, the std::cin >> iVal call just set the stream into a fail state all over again.
    Last edited by hk_mp5kpdw; 03-08-2005 at 05:28 PM.
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  5. #5
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    "The reason you still can't enter any input after clearing the error flags is that the bad value is still in the stream. In my case above the 'r' character never gets extracted and the next time through the loop, the std::cin >> iVal call just set the stream into a fail state all over again."

    So, if you haven't already figured out what to do after reading the above yet, call cin.ignore() after you clear() the stream to ignore the offending char. The problem is you don't know how many char to ignore, (who knows how many char the user entered?) so ignore as many as char as the stream could potentially hold using something like:

    std::cin.ignore(std::numeric_limits<std::streamsiz e>::max());

    or some such similarly crazy syntax (often you can just some reasonbly large value instead of all the mumbo jumbo used above, but you're probably better off using something exuberant (sophisticated?, overkill?) like the above.
    You're only born perfect.

  6. #6
    former member Brain Cell's Avatar
    Join Date
    Feb 2004
    Posts
    472
    I think i understand how the endLine worked. Correct me if i'm wrong :

    Code:
    ostream& ostream::operator << ( ostream& (*op)(ostream&))
    {
        // call the function passed as parameter with this stream as the argument
        return (*op)(*this);
    }
    endLine isn't called AFTER this function executes. ostream:perator<< called it with an argument of ostream in its return value , am i getting it right?


    for the second question , i tried discarding the invalid character(s) from the stream then clearing the flags , but it still won't work. See this code for example :
    Code:
    	int x , y;
    
    	cin >> x;
    
    	cin.ignore(2);
    	cin.clear();
    
    	cin >> y;
    
    	cout << x << ',' << y << endl;

    p.s : sorry for the late response. I was kinda busy.
    My Tutorials :
    - Bad programming practices in : C
    - C\C++ Tips
    (constrcutive criticism is very welcome)


    - Brain Cell

  7. #7
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    I'd try something like this:
    Code:
    bool collect = true;
    int x;
     
    while(collect)
    {
      cout << "enter integer value of x" << endl;
      cin >> x;
      if(cin.fail())
      {
    	cin.ignore(1000);
    	cin.clear();
      }
      else 
    	collect = false;
    }
    You're only born perfect.

  8. #8
    former member Brain Cell's Avatar
    Join Date
    Feb 2004
    Posts
    472
    i still get hundreds of "enter integer value of x" when i enter a character
    My Tutorials :
    - Bad programming practices in : C
    - C\C++ Tips
    (constrcutive criticism is very welcome)


    - Brain Cell

  9. #9
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    compiled and ran my code. got same problem you did. so switched these two lines:

    cin.ignore(1000);
    cin.clear();

    and ended up having to ignore 1000 entries before being able to proceed. so used this:

    cin.clear();
    cin.ignore();

    and everything worked out ok unless I enter two char in a row, like fg instead of f. Then I got two "enter integer value of x" blurbs, but I can live with that.

    Data validation is never fun/easy.
    You're only born perfect.

  10. #10
    Registered User
    Join Date
    Aug 2002
    Location
    Hermosa Beach, CA
    Posts
    446
    Another way of handling this is to read in all string data, validate with a string function, then convert to integer with stringstream. You could then print out something like, "Invalid input, 'rr' ignored". For example, the following worked for me:

    Code:
    # include <iostream>
    # include <sstream>
    # include <string>
    using namespace std;
    
    int main()
    {
    	string xstr;
    	int x;
    	bool invalid = true;
    
    	while (invalid) {
    		cout << "Enter an integer: ";
    		cin >> xstr;
    
    		if (!cin.good()) {
    			// do error checking here, but this should 
    			// almost always be successful...
    		}
    
    		size_t pos = xstr.find_first_not_of("0123456789-");
    		if (pos != string::npos) {
    			// Invalid input
    			cout << "Invalid input ignored, " << xstr << endl;
    		}
    		else 
    			invalid = false;
    	}
    
    	stringstream ss;
    	ss << xstr;
    	ss >> x;
    	cout << "x value is " << x << endl;
        return 0;
    }
    The crows maintain that a single crow could destroy the heavens. Doubtless this is so. But it proves nothing against the heavens, for the heavens signify simply: the impossibility of crows.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. A very long list of questions... maybe to long...
    By Ravens'sWrath in forum C Programming
    Replies: 16
    Last Post: 05-16-2007, 05:36 AM
  2. A couple of stream questions
    By AngKar in forum C# Programming
    Replies: 4
    Last Post: 06-20-2006, 03:42 PM
  3. Help! About text stream and binary stream
    By Antigloss in forum C Programming
    Replies: 1
    Last Post: 09-01-2005, 08:40 AM
  4. Trivial questions - what to do?
    By Aerie in forum A Brief History of Cprogramming.com
    Replies: 23
    Last Post: 12-26-2004, 08:44 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21