Input Stream Questions

Please support our C++ advertiser: Intel Parallel Studio Home
Thread Solved

Join Date: Feb 2008
Posts: 67
Reputation: c++noobie is an unknown quantity at this point 
Solved Threads: 1
c++noobie c++noobie is offline Offline
Junior Poster in Training

Input Stream Questions

 
0
  #1
Oct 21st, 2008
I have a feeling ya'll get plenty of cin questions here and are quit tired of them, but I have a few that are a little more in depth. I started trying to write my own input stream flush template and had some very minor successes when I found the sticky at the top of this and found a break through. Here is what I wrote after the breakthrough
  1. template <typename CharT>
  2. void flush(std::basic_istream<CharT> &in)
  3. {
  4. in.clear();
  5. char nextChar= in.get();
  6. while(nextChar != '\n' && nextChar != std::char_traits<CharT>::eof())
  7. nextChar= in.get();
  8. in.clear();
  9. }
  10.  
  11. template <typename CharT, typename var_type>
  12. void extractr(std::basic_istream<CharT> &in, var_type &var, bool FLUSH= false)
  13. {
  14. if(!in)
  15. flush(in);
  16. in >> var;
  17. while(!in)
  18. {
  19. cout << "\nInvalid data type, please enter a " << typeid(var).name() << endl << "-> ";
  20. flush(in);
  21. in >> var;
  22. }
  23. if(FLUSH)
  24. flush(in);
  25. }
I played around with the solution the sticky offered and got to this, but it doesn't work in my codes.
  1. template <typename CharT>
  2. void ignore_line(std::basic_istream<CharT> &in)
  3. {
  4. in.clear();
  5. if(in.rdbuf()->sungetc() != std::char_traits<CharT>::eof() && in.get() != '\n')
  6. in.ignore(std::numeric_limits<std::streamsize>::max(), in.widen('\n'));
  7. in.clear();
  8. }

Question 1) What exactly does widen() do? I've seen it several times but don't really understand what it is accomplishing.

Question 2) Why does the first one work perfectly but the second one not fix the input stream?
Last edited by c++noobie; Oct 21st, 2008 at 4:31 pm.
Reply With Quote Quick reply to this message  
Join Date: Sep 2004
Posts: 7,663
Reputation: Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute 
Solved Threads: 725
Team Colleague
Narue's Avatar
Narue Narue is offline Offline
Code Goddess

Re: Input Stream Questions

 
1
  #2
Oct 21st, 2008
>Question 1) What exactly does widen() do?
It takes the character you pass and converts it to the locale the stream is imbued with. For example, you want to convert a character literal to the appropriate wchar_t value if the stream is wide oriented, but leave it as is if the stream is not wide oriented.

>Question 2) Why does the first one work perfectly
>but the second one not fix the input stream?
It's hard to say without seeing how you're testing the code, but I'd guess you're immediately entering invalid data. Because no characters have been successfully extracted, sungetc returns eof() and the ignore isn't performed. The effect is the same as if the stream were empty.

The first function works because you're extracting characters and not relying on the previously extracted character, which might not exist for this particular usage, but it basically negates the solution provided in the second function because you're back to blocking reads.
I'm here to prove you wrong.
Reply With Quote Quick reply to this message  
Join Date: Feb 2008
Posts: 67
Reputation: c++noobie is an unknown quantity at this point 
Solved Threads: 1
c++noobie c++noobie is offline Offline
Junior Poster in Training

Re: Input Stream Questions

 
0
  #3
Oct 22nd, 2008
I've played around with some inputs and such looking at it from that point of view and it makes sense. I still don't fully understand why it remains in an error state, even if the ignore() is ignored.
  1. void main()
  2. {
  3. flush(cin);
  4. char c;
  5. cout << "char? ";
  6. extractr(cin, c);
  7. cout << "char: " << c << endl;
  8. int var;
  9. cout << "int? ";
  10. extractr(cin, var);
  11. cout << "int: " << var << endl;
  12. }
When using flush, this will continue to ask for a corrected input until it receives one, but it will pause at the beginning of the program because it needs something for in.get(). When using ignore_line, this will ask for corrected input once after receiving incorrect int input (or using leftover from something like "asdf" for char input) but will enter an infinite loop (due to error state in cin) after any further incorrect input. Two questions again, why does it behave like that? and is there a usable and possibly portable solution?
Last edited by c++noobie; Oct 22nd, 2008 at 2:21 am.
Reply With Quote Quick reply to this message  
Join Date: Sep 2004
Posts: 7,663
Reputation: Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute 
Solved Threads: 725
Team Colleague
Narue's Avatar
Narue Narue is offline Offline
Code Goddess

Re: Input Stream Questions

 
1
  #4
Oct 22nd, 2008
>I still don't fully understand why it remains in an error state, even if the ignore() is ignored.
It doesn't remain in an error state, but because the bad data isn't extracted, you immediately enter the same error state again as soon as you say in >> var; in extractr after trying to ignore the line.

>why does it behave like that?
If the stream is empty, sungetc will return eof(). If the stream is not empty and the last extracted character is not '\n', the entire line will be ignored. Here's what happens with "asdf" as the first input and "hjkl" as the second input:
  1. 'a' is extracted by the first call of extractr.
  2. 's' causes the second call of extractr to fail (it's not an int) and because the last extracted character was 'a', the condition in ignore_line is met and ignore is called. This extracts the rest of the line and leaves the stream empty.
  3. extractr prompts for correct input, but the next character is 'h', which fails because it isn't an int. However, because the stream is empty, sungetc returns eof() and the ignore isn't performed. Nowhere else in the error loop of extractr do you clear out bad characters, so the loop will run infinitely by erroring on 'h'.
>is there a usable and possibly portable solution?
Add another level of abstraction where you can count the number of times ignore_line fails to clear the stream. If it does so more than a set number of times, which you would specify as the threshold for an infinite loop, force ignore to be called:
  1. template <typename CharT>
  2. bool flush ( std::basic_istream<CharT>& in, bool always_flush )
  3. {
  4. in.clear();
  5.  
  6. if ( always_flush
  7. || in.rdbuf()->sungetc() != std::char_traits<CharT>::eof()
  8. && in.get() != '\n' )
  9. {
  10. in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );
  11. return true;
  12. }
  13.  
  14. in.clear();
  15.  
  16. return false;
  17. }
  18.  
  19. template <typename CharT>
  20. void flush ( std::basic_istream<CharT>& in )
  21. {
  22. int failed = 0;
  23.  
  24. while ( !flush ( in, false ) ) {
  25. if ( ++failed > 1 ) {
  26. flush ( in, true );
  27. break;
  28. }
  29. }
  30. }
I'm here to prove you wrong.
Reply With Quote Quick reply to this message  
Join Date: Feb 2008
Posts: 67
Reputation: c++noobie is an unknown quantity at this point 
Solved Threads: 1
c++noobie c++noobie is offline Offline
Junior Poster in Training

Re: Input Stream Questions

 
0
  #5
Oct 22nd, 2008
First of all, thank you so much for all of this help, it's really helping me understand all of this.

Second, I did a lot more looking into it and reading, and I think I came up with something (closely related to your ignore and pause classes as I looked at those quite a bit).
  1. template <typename CharT>
  2. std::streamsize ignore_line( std::basic_istream<CharT> &in, bool always_ignore= false )
  3. {
  4. std::streamsize nread= 0;
  5.  
  6. in.clear();
  7.  
  8. if( always_ignore || ( in.rdbuf()->sungetc() != std::char_traits<CharT>::eof() && in.get() != '\n' ) )
  9. {
  10. in.ignore( std::numeric_limits<std::streamsize>::max(), in.widen('\n') );
  11. nread= in.gcount();
  12. }
  13.  
  14. in.clear();
  15.  
  16. return nread;
  17. }
  18.  
  19. template <typename var_type>
  20. class extract
  21. {
  22. var_type *var;
  23. bool _always_flush, _full_count;
  24. mutable std::streamsize *_nread;
  25. public:
  26. extract( var_type &input, bool always_flush= false )
  27. : _always_flush(always_flush), _nread(0), var(&input), _full_count(false)
  28. {}
  29.  
  30. extract( var_type &input, std::streamsize &nread, bool always_flush= false )
  31. : _always_flush(always_flush), _nread(&nread), var(&input), _full_count(false)
  32. {*_nread=0;}
  33.  
  34. //_nread[0] holds pre-extraction character ignore count
  35. //_nread[1] holds mid-extraction character ignore count
  36. //_nread[2] holds post-extraction character ignore count
  37. extract( var_type &input, std::streamsize nread[3], bool always_flush= false )
  38. : _always_flush(always_flush), _nread(nread), var(&input), _full_count(true)
  39. {_nread[0]=0;_nread[1]=0;_nread[2]=0;}
  40.  
  41. template <typename CharT>
  42. friend std::basic_istream<CharT> &operator>> ( std::basic_istream<CharT> &in, const extract &manip )
  43. {
  44. int _ignore_count= 0;
  45.  
  46. if( manip._always_flush || !in )
  47. manip._nread[0]+=ignore_line(in);
  48.  
  49. in >> *manip.var;
  50. while(!in)
  51. {
  52. cout << "\nInvalid data type, please enter a value of type: "
  53. << typeid(*manip.var).name() << "\n-> ";
  54. _ignore_count+= ignore_line(in, true);
  55. in >> *manip.var;
  56. }
  57.  
  58. if(manip._full_count)
  59. {
  60. manip._nread[1]= _ignore_count;
  61. _ignore_count= 0;
  62. }
  63.  
  64. if(manip._always_flush)
  65. _ignore_count+= *manip._nread= ignore_line(in);
  66.  
  67. if(manip._full_count)
  68. manip._nread[2]= _ignore_count;
  69.  
  70. return in;
  71. }
  72. };
  73.  
  74. void main()
  75. {
  76. int i;
  77. char c;
  78. string s;
  79. std::streamsize chars[3];
  80.  
  81. cout << "char? ";
  82. cin >> extract<char>(c);
  83. cout << "char: " << c << endl;
  84. cout << "int? ";
  85. cin >> extract<int>(i, chars, true);
  86. cout << "int: " << i << endl << "ignored: " << chars[0] << ' ' << chars[1] << ' ' << chars[2] << endl;
  87. cout << "string? ";
  88. cin >> extract<string>(s);
  89. cout << "string: " << s << endl;
  90. }
It works well, although, I am unsure about the parts involving _nread. If there is a better way to do this, please let me know. Also, whenever I enter "asdf" for the first input, the first ignore count comes out to be 1 when it seems to me that it should be 4 (ignoring 'sdf\n'). Again, thank you so much for all of this, I think I'm understanding all of these concepts quite a bit better now.
Reply With Quote Quick reply to this message  
Join Date: Sep 2004
Posts: 7,663
Reputation: Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute Narue has a reputation beyond repute 
Solved Threads: 725
Team Colleague
Narue's Avatar
Narue Narue is offline Offline
Code Goddess

Re: Input Stream Questions

 
1
  #6
Oct 22nd, 2008
>whenever I enter "asdf" for the first input, the first ignore count comes
>out to be 1 when it seems to me that it should be 4 (ignoring 'sdf\n')
Run it through a debugger and check the values returned by ignore_line. I imagine you'll find them to be correct, and that should help you pinpoint the real problem.
I'm here to prove you wrong.
Reply With Quote Quick reply to this message  
Join Date: Feb 2008
Posts: 67
Reputation: c++noobie is an unknown quantity at this point 
Solved Threads: 1
c++noobie c++noobie is offline Offline
Junior Poster in Training

Re: Input Stream Questions

 
0
  #7
Oct 23rd, 2008
Found it...Thanks so much for all your help Narue. I think I have a better grasp on all of this now.
Reply With Quote Quick reply to this message  
Reply

This thread has been marked solved.
Perhaps start a new thread instead?
Message:


Thread Tools Search this Thread



About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC