Кин, не дожидаясь ввода?
Для проекта, над которым я работаю, мне нужна программа, чтобы иметь возможность получать ввод от пользователя, но пока он что-то вводит, программа может продолжать цикл.
Например:while (true)
{
if (userInput == true)
{
cin >> input
}
//DO SOMETHING
}
Это будет означать, что //DO SOMETHING
будет происходить каждый цикл, без нажатия пользователем клавиши ввода миллион раз.
kbhit()
а также getch()
из conio.h, но это очень запутанно, и я не люблю использовать conio.h по причинам переносимости и т. д. Кроме того, ему не нужно использовать cin
в частности, потому что есть большая вероятность, что он просто не будет работать с ним, поэтому любое хорошее решение, которое не требует от меня внесения собственного вклада с "не очень хорошей" библиотекой, будет высоко цениться.
4 ответа
Печально, что не существует простого переносимого способа асинхронной проверки, нажата ли клавиша. Но я думаю, что стандартная комиссия тщательно оценила все за и против.
Если вы не хотите полагаться на сторонние библиотеки управления событиями, и если многопоточность будет излишней, одной из альтернатив может быть ваша собственная версия kbhit()
с условной компиляцией для сред, которые вы хотите поддерживать:
- если ваш conio.h поддерживает kbhit(), просто используйте его.
- для окон вы можете обратиться к _kbhit()
- для linux и posix вы можете использовать ответ Матье или посмотреть здесь код Моргана Мэтьюса
Это не самый академический ответ, но он прагматичный.
Это может стоить изучить многопоточность для этого. Я обычно не решаюсь предложить это, потому что многопоточность влечет за собой множество потенциальных проблем, которые могут оказаться трудными для отладки, но в этом случае они могут быть изолированы довольно легко. Я представляю что-то вроде этого:
#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
с общими данными происходит, когда не запущен ни один поток, который может обойти основной цикл.
Отказ от ответственности: похоже, что следующее работает с gcc в Linux, однако по некоторым причинам он не работает с VC++ в Windows. Спецификации, кажется, дают много возможностей для реализации здесь, и VC++ определенно использует это в своих интересах...
Есть несколько функций, доступных на любой std::basic_istream
или его основа std::basic_streambuf
,
Чтобы узнать, есть ли какой-либо символ, доступный для ввода, вы можете позвонить in_avail
на std::basic_streambuf
:
if (std::cin.rdbuf() and std::cin.rdbuf()->in_avail() >= 0) {
}
in_avail
дает количество символов, доступных без блокировки, он возвращает -1
если нет такого персонажа. После этого вы можете использовать обычные "отформатированные" операции чтения, такие как std::cin >> input
,
В противном случае, для неформатированных чтений, вы можете использовать readsome
от std::basic_istream
, который возвращает до N символов, доступных без блокировки:
size_t const BufferSize = 512;
char buffer[BufferSize];
if (std::cin.readsome(buffer, BufferSize) >= 1) {
}
Однако следует отметить, что реализация этого метода сильно варьируется, поэтому для переносимой программы это может быть не очень полезно.
Примечание: как уже упоминалось в комментарии, in_avail
подход может быть пятнистым. Я подтверждаю, что это может работать, однако сначала вы должны использовать скрытую функцию потоков ввода-вывода C++: std::ios_base::sync_with_stdio(false)
который позволяет потокам C++ буферизовать ввод (и таким образом украсть его из буферов stdio C).
Может быть, вы можете попробовать:
while (true)
{
if (userInput == true)
{
if(cin >> input){
}else{
std::cout << "Invalid input!" << std::endl;
cin.clear();
}
}
//DO SOMETHING
}