Je cherche un moyen (multiplateforme) d'effectuer une entrée de console non bloquante pour mon programme C++, afin de pouvoir gérer les commandes utilisateur pendant l'exécution continue du programme. Le programme produira également des informations en même temps.
Quelle est la meilleure façon/la plus facile de faire cela? Je n'ai aucun problème à utiliser des bibliothèques externes comme boost, dans la mesure où elles utilisent une licence permissive.
Je le ferais en créant un thread distinct qui appelle des fonctions de blocage normales IO et lui transmet une fonction de rappel qu’il appellerait lorsqu’il aurait une entrée. Êtes-vous sûr de devoir faire ce que vous avez dit vouloir faire?
En ce qui concerne la sortie d'informations en même temps, que se passerait-il si l'utilisateur était en train de saisir une entrée et que vous imprimiez quelque chose?
Je l'ai fait sur QNX4.5 qui ne supporte pas les threads ou Boost en utilisant select
. Vous passez essentiellement select
STDIN en tant que descripteur de fichier à utiliser, puis sélectionnez cette option pour renvoyer une nouvelle ligne. J'ai ajouté un exemple de boucle simplifié ci-dessous. Elle est indépendante de la plate-forme, du moins pour les systèmes de type Unix. Pas sûr de Windows cependant.
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);
}
}
}
Il y a un moyen simple:
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);
}
}
Pas de bloc, tout en 1 fil. Quant à moi, ça marche.
Entrée console non bloquante C++?
Réponse: console IO sur un thread d'arrière-plan et fournit un moyen de communication entre les threads.
Voici un programme de test complet (mais simpliste) qui implémente asio asynchrone en reportant l'io à un thread d'arrière-plan.
le programme attend que vous saisissiez des chaînes (terminaison avec nouvelle ligne) sur la console, puis effectuez une opération de 10 secondes avec cette chaîne.
vous pouvez entrer une autre chaîne pendant que l'opération est en cours.
entrez 'quit' pour que le programme s'arrête au cycle suivant.
#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;
}
exemple de test:
$ ./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?
Exemple utilisant C++ 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);
}
compilateur en ligne: http://rextester.com/XGX58614
ncurses peut être un bon candidat.
La/ StdinDataIO class de la bibliothèque sous licence BSD MUSCLE prend en charge les lectures non bloquantes à partir de stdin sous Windows, MacOS/X et Linux/Unix ... vous pouvez l'utiliser (ou simplement examiner le code). comme exemple de la façon dont cela peut être fait) si vous voulez.
Vous pouvez utiliser la bibliothèque tinycon pour ce faire. Il suffit de générer un objet tinycon dans un nouveau fil de discussion et vous avez pratiquement terminé. Vous pouvez définir la méthode de déclenchement pour déclencher tout ce que vous souhaitez lorsque vous appuyez sur Entrée.
Vous pouvez le trouver ici: https://sourceforge.net/projects/tinycon/
En outre, la licence est BSD, ce sera donc la plus permissive pour vos besoins.
libuv est une bibliothèque C multiplateforme pour les E/S asynchrones. Il utilise une boucle d'événement pour lire, par exemple, l'entrée standard sans bloquer le thread. libuv est ce qui alimente Node.JS et les autres.