Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cin without waiting for input?

Tags:

c++

For a project I am working on, I need the program to be able to receive input from the user, but while they are inputting something, the program can continue in the loop.

For example:
while (true)
{
    if (userInput == true)
    {
        cin >> input
    }
    //DO SOMETHING
}

This would mean that //DO SOMETHING would happen every loop, without the user pressing enter a million times.

Before, my solution was creating my own input using kbhit() and getch() from conio.h, but that got very messy, and I don't like using conio.h for portability reasons etc. Also, it doesn't need to use cin specifically, because there is a good chance it just wouldn't work with it, so any good solution that doesn't require me making my own input with a 'not very good' library, would be much appreciated.
like image 830
Orfby Avatar asked Dec 20 '14 17:12

Orfby


People also ask

How do you make Cin not wait?

Use cin. clear(); and cin. ignore(numeric_limits<streamsize>::max(), '\n'); to limit an input to int 's only.

How do you take input without entering?

Use the _getch() function to give you a character without waiting for the Enter key.

Does CIN automatically create a new line?

cin automatically generates a newline.

How do you take input until Enter is pressed in C++?

The standard C++ I/O libraries only have functions that read input from a stream, which means to get a string on a console, enter key should be pressed. If you want to get the input without pressing enter key, you have to use nonstandard functions like _getch() in <conio. h> in Windows.


2 Answers

It could be worth looking into multi-threading for this. I'm usually hesitant to suggest it, because multithreading pulls in a host of potential problems that can end up difficult to debug, but in this case they can be isolated fairly easily. I envision something like this:

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

int main() {
  std::atomic<bool> interrupted;
  int x;
  int i = 0;

  do {
    interrupted.store(false);

    // create a new thread that does stuff in the background
    std::thread th([&]() {
        while(!interrupted) {
          // do stuff. Just as an example:
          std::cout << i << std::flush;
          std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
      });

    std::cin >> x;

    // when input is complete, interrupt thread and wait for it to finish
    interrupted.store(true);
    th.join();

    // apply x, then loop to make new thread to do stuff
    i = x;
  } while(x != -1); // or some other exit condition
}

At first glance this looks somewhat wasteful because it keeps spawning and throwing threads away, but user input takes, in computing terms, an eternity, so the overhead should not be prohibitive. More importantly, it does have the advantage of avoiding any suggestion of data races wholesale because the only means of communication between the main (input) loop and the background thread is the atomic interruption flag, and the application of x to shared data happens when no thread is running that could race the main loop.

like image 97
Wintermute Avatar answered Sep 30 '22 16:09

Wintermute


Disclaimer: the following seems to be working with gcc on Linux, however for some reasons it does not work with VC++ on Windows. The specifications appear to give a lot of leeway to the implementations here, and VC++ definitely takes advantage of it...

There are multiple functions available on any std::basic_istream or its underlying std::basic_streambuf.

In order to know if there is any character available for input, you may call in_avail on std::basic_streambuf:

if (std::cin.rdbuf() and std::cin.rdbuf()->in_avail() >= 0) {
}

in_avail gives you the number of characters available without blocking, it returns -1 if there is no such character. Afterward, you can use the regular "formatted" read operations such as std::cin >> input.

Otherwise, for unformatted reads, you can use readsome from std::basic_istream, which returns up to N characters available without blocking:

size_t const BufferSize = 512;
char buffer[BufferSize];

if (std::cin.readsome(buffer, BufferSize) >= 1) {
}

However it is noted that the implementation of this method is highly variable, so for a portable program it might not be that useful.

Note: as mentioned in the comment, the in_avail approach might be spotty. I confirm it can work, however you first have to use an obscure feature of C++ IO streams: std::ios_base::sync_with_stdio(false) which allows C++ streams to buffer input (and thus steal it from C's stdio buffers).

like image 31
Matthieu M. Avatar answered Sep 30 '22 16:09

Matthieu M.