Cin无需等待输入?

时间:2014-12-20 17:16:03

标签: c++

对于我正在处理的项目,我需要程序能够接收来自用户的输入,但是当他们输入内容时,程序可以继续循环。

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

这意味着每个循环都会发生//DO SOMETHING,而用户不会按百万次输入。

以前,我的解决方案是使用来自conio.h的kbhit()getch()创建自己的输入,但是这非常混乱,我不喜欢使用conio.h以便于携带等。此外,它不需要特别使用cin,因为很有可能它不能使用它,所以任何好的解决方案都不需要我使用'不太好'的库来自己输入,将不胜感激。

4 个答案:

答案 0 :(得分:2)

免责声明:以下似乎与Linux上的gcc一起使用,但由于某些原因,它无法在Windows上与VC ++一起使用。规范似乎为这里的实现提供了很大的余地,VC ++肯定会利用它......

任何std::basic_istream或其基础std::basic_streambuf都有多种功能。

为了了解是否有任何字符可供输入,您可以在std::basic_streambuf上致电in_avail

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

in_avail为您提供无阻塞的可用字符数,如果没有此类字符,则返回-1。之后,您可以使用常规"格式化"读取std::cin >> input等操作。

否则,对于未格式化的读取,您可以使用std::basic_istream中的readsome,其中最多可返回N个字符而不会阻止:

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

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

然而,值得注意的是,这种方法的实现变化很大,因此对于便携式程序来说,它可能没那么有用。

注意:如评论中所述,in_avail方法可能不稳定。我confirm it can work,但是你首先必须使用C ++ IO流的一个不起眼的特性:std::ios_base::sync_with_stdio(false)它允许C ++流缓冲输入(从而从C&#stdio缓冲区中窃取它)。

答案 1 :(得分:2)

为此可能值得研究多线程。我通常犹豫不决,因为多线程会引发许多可能最终难以调试的潜在问题,但在这种情况下,它们可以相当容易地被隔离。我设想这样的事情:

#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
}

乍一看,这看起来有点浪费,因为它会产生并抛弃线程,但是用户输入在计算方面需要永恒,因此开销不应过高。更重要的是,它确实具有避免任何数据竞争建议的优势,因为主(输入)循环和后台线程之间唯一的通信方式是原子中断标志,以及x对共享的应用当没有运行的线程可能会使主循环竞争时会发生数据。

答案 2 :(得分:1)

令人遗憾的是,如果按键被击中,则没有简单的可移植方式来异步检查。但我想标准委员会已经仔细评估了利弊。

如果您不想依赖第三方事件管理库,并且如果多线程会过度,那么可以选择拥有自己的kbhit()版本,并为您想要的环境进行条件编译支持:

  • 如果你的conio.h支持kbhit()只是使用它。
  • 对于Windows,您可以参考_kbhit()
  • 对于linux和posix,您可以使用Matthieu的答案,或者查看here获取Morgan Mattews的代码

这不是最具学术性的答案,但它是务实的。

答案 3 :(得分:0)

也许,你可以试试:

while (true)
{
    if (userInput == true)
    {
        if(cin >> input){
        }else{
            std::cout << "Invalid input!" << std::endl;
            cin.clear();
        }
    }
    //DO SOMETHING
}