Подтвердить что ты не робот

Неблокирующий вход консоли С++

Я ищу (мультиплатформенный) способ сделать неблокирующий ввод в консоль для моей С++-программы, поэтому я могу обрабатывать пользовательские команды, пока программа постоянно работает. Программа также будет выводить информацию одновременно.

Какой лучший/самый простой способ сделать это? У меня нет проблем с использованием внешних библиотек, таких как boost, если они используют разрешающую лицензию.

4b9b3361

Ответ 1

Я бы сделал это, создав отдельный поток, который вызывает нормальные блокирующие функции ввода-вывода и передает ему функцию обратного вызова, которую он вызывал бы, когда он был введен. Вы уверены, что вам нужно делать то, что вы сказали, что хотите сделать?

Что касается вывода информации одновременно, что произойдет, если пользователь окажется в середине ввода некоторого ввода и вы что-то напечатали?

Ответ 2

Я сделал это на QNX4.5, который не поддерживает потоки или Boost с помощью select. Вы в основном передаете select STDIN в качестве дескриптора файла, который будет использоваться, и select вернется, когда будет введена новая строка. Я добавил упрощенный примерный цикл ниже. Он независим от платформы, по крайней мере для Unix-подобных систем. Однако не уверен в Windows.

while (!g_quit)
{
   //we want to receive data from stdin so add these file
   //descriptors to the file descriptor set. These also have to be reset
   //within the loop since select modifies the sets.
   FD_ZERO(&read_fds);
   FD_SET(STDIN_FILENO, &read_fds);

   result = select(sfd + 1, &read_fds, NULL, NULL, NULL);
   if (result == -1 && errno != EINTR)
   {
      cerr << "Error in select: " << strerror(errno) << "\n";
      break;
   }
   else if (result == -1 && errno == EINTR)
   {
      //we've received and interrupt - handle this
      ....
   }
   else
   {
      if (FD_ISSET(STDIN_FILENO, &read_fds))
      {
         process_cmd(sfd);
      }
   }
}

Ответ 3

Существует один простой способ:

char buffer[512];
int point = 0;
...
while (_kbhit()) {
    char cur = _getch();
    if (point > 511) point = 511;
    std::cout << cur;
    if (cur != 13) buffer[point++] = cur;
    else{
        buffer[point] = '\0';
        point = 0;
        //Run(buffer);
    }
}

Без блока, все в 1 потоке. Что касается меня, это работает.

Ответ 4

Неблокирующий вход консоли С++?

Ans: выполните консоль ввода-вывода в фоновом потоке и обеспечьте средство связи между потоками.

Здесь полная (но упрощенная) тестовая программа, которая реализует async io, отложив io на фоновый поток.

программа будет ждать ввода строк (завершение с новой строкой) на консоли, а затем выполнить 10-секундную операцию с этой строкой.

вы можете ввести другую строку во время выполнения операции.

введите 'quit', чтобы программа остановилась в следующем цикле.

#include <iostream>
#include <memory>
#include <string>
#include <future>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <deque>

int main()
{
    std::mutex m;
    std::condition_variable cv;
    std::string new_string;
    bool error = false;

    auto io_thread = std::thread([&]{
        std::string s;
        while(!error && std::getline(std::cin, s, '\n'))
        {
            auto lock = std::unique_lock<std::mutex>(m);
            new_string = std::move(s);
            if (new_string == "quit") {
                error = true;
            }
            lock.unlock();
            cv.notify_all();
        }
        auto lock = std::unique_lock<std::mutex>(m);
        error = true;
        lock.unlock();
        cv.notify_all();
    });

    auto current_string = std::string();
    for ( ;; )
    {
        auto lock = std::unique_lock<std::mutex>(m);
        cv.wait(lock, [&] { return error || (current_string != new_string); });
        if (error)
        {
            break;
        }
        current_string = new_string;
        lock.unlock();

        // now use the string that arrived from our non-blocking stream
        std::cout << "new string: " << current_string;
        std::cout.flush();
        for (int i = 0 ; i < 10 ; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << " " << i;
            std::cout.flush();
        }
        std::cout << ". done. next?\n";
        std::cout.flush();
    }
    io_thread.join();
    return 0;
}

пробный прогон:

$ ./async.cpp
first
new string: first 0 1las 2t 3
 4 5 6 7 8 9. done. next?
new string: last 0 1 2 3 4 5 6 7 8quit 9. done. next?

Ответ 5

ncurses может быть хорошим кандидатом.

Ответ 6

StdinDataIO класс BSD-лицензированной Сетевая библиотека MUSCLE поддерживает неблокирующие чтения из stdin под Windows, MacOS/X и Linux/Unix... вы можете использовать это (или просто изучить код как пример того, как это можно сделать), если хотите.

Ответ 7

Для этого вы можете использовать библиотеку tinycon. Просто создайте объект tinycon в новом потоке, и вы в значительной степени сделали. Вы можете определить метод триггера, чтобы отключить все, что вам нужно, когда нажата клавиша ввода.

Вы можете найти его здесь: https://sourceforge.net/projects/tinycon/

Кроме того, лицензия является BSD, поэтому она будет наиболее разрешительной для ваших нужд.

Ответ 8

libuv - это кросс-платформенная библиотека C для асинхронного ввода-вывода. Он использует цикл событий, чтобы делать что-то вроде чтения со стандартного ввода без блокировки потока. libuv - это силы Node.JS и другие.

Ответ 9

Пример использования С++ 11:

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

static std::string getAnswer()
{    
    std::string answer;
    std::cin >> answer;
    return answer;
}

int main()
{
    int timeout = 5;
    std::cout << "do you even lift?" << std::endl;
    std::string answer = "maybe"; //default to maybe
    std::future<std::string> future = std::async(getAnswer);
    if (future.wait_for(std::chrono::seconds(timeout)) == std::future_status::ready)
        answer = future.get();

    std::cout << "the answer was: " << answer << std::endl;
    exit(0);
}

онлайн-компилятор: http://rextester.com/XGX58614