Select, std :: cin and std :: getline don't play well together

Problem:

My program is trying to mix 'std :: cin <<...' and std :: getline with a dash 'select'. It has strange behavior, but not the typical "you need std :: cin.ignore" situation.

Desired operation:

The intention is to read tokens from std :: cin (if any) until a "stop" token is found. Anything that remains in std :: cin is read later as the answer to the question. For example:

$ echo 'one two stop y' | ./a.out
read 'one'
read 'two'
Question? y
read 'y'
$

      

The tokens "one" and "two" are shown, then "stop", which prevents reading "y". Only when a question is asked should "y" be read.

Actual operation:

read 'one two stop y'
Question? read ''
Question? read ''
Question? read ''
...

      

The program first calls "select" to check if there are pending tokens, then it uses std :: cin, then std :: getline. This is usually where you might think I need to read what the pending "\ n" std :: getline does not read. But the selection does not indicate that there is any input pending, so std :: cin skips it, and std :: getline gets it all.

However, if timeout is added, it works fine:

  tv.tv_usec = 1000;

      

Finally, the question is:

What's happening? And what stupid mistakes have I made?

Code:

#include <iostream>
#include <sys/select.h>

int main (int argc, char* argv[])
{
  // Step 1, read until 'stop'.
  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 0;

  fd_set fds;
  FD_ZERO (&fds);
  FD_SET (STDIN_FILENO, &fds);

  int result = select (STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
  if (result && result != -1)
  {
    if (FD_ISSET (0, &fds))
    {
      std::string token;
      while (std::cin >> token)
      {
        if (token == "stop")
          break;

        std::cout << "read '" << token << "'\n";
      }
    }
  }

  // Step 2, ask a question.
  std::string answer;
  do
  {
    std::cout << "Question? ";
    std::getline (std::cin, answer);
    std::cout << "read '" << answer << "'\n";
  }
  while (answer != "y");

  return 0;
}

      

+3


source to share


2 answers


If you have tv.tv_usec = 0 then this is just a poll to see if there is something. Something must exist for the file statement to return something other than 0 (which indicates that the time was reached with no inputs available).

Another problem that I observe is that with tv.tv_usec and tv.tv_sec = 0 you only check once if there is data available to process. If the input FD is not set when you reach the selection it will simply fail (result is zero) and then exit the program. I suspect that setting the time on the TV gives you the opportunity to enter something to set the handle. I would put select inside a loop to wait for input. As the program is currently written, the selection will only be checked once to see if there is an input. I have a loop example below.



while ( 1 ) 
{
    FD_ZERO (&fds);   
    FD_SET (STDIN_FILENO, &fds);
    tv.tv_sec = 0; 
    tv.tv_usec = 100;
    result = select (STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
    // Code here to read in line if result is not zero

}

      

I would put in some time to wait as well, or you will just loop very fast. Select will return when the input appears and the TV will show the remaining time.

+3


source


Most of the mixing problems of cin and cin.getline are the results or side effects of a flush problem . I don't think it will be any different in this scenario.



EDIT: And yes, you have to use peek to check for the existence of a character without extracting it from the stream.

+1


source







All Articles