2014-12-20 12 views
5

Per un progetto su cui sto lavorando, ho bisogno che il programma sia in grado di ricevere input dall'utente, ma mentre sta inserendo qualcosa, il programma può continuare nel ciclo.Cin senza aspettare input?

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

Ciò significherebbe che //DO SOMETHING sarebbe successo ogni ciclo, senza che l'utente preme entrare un milione di volte.

Prima, la mia soluzione era creare il mio ingresso usando kbhit() e getch() da conio.h, ma che ha ottenuto molto disordinato, e non mi piace usare conio.h per ragioni di portabilità, ecc Inoltre, non ha bisogno di usare lo cin in modo specifico, perché ci sono buone probabilità che non funzionerebbe con esso, quindi qualsiasi buona soluzione che non richieda di effettuare il mio input con una libreria "non molto buona", sarebbe molto apprezzata.
+2

Forse hai bisogno di una sorta di I/O asincrono? Come con epoll o kqueue o libevent2? –

+0

Vuoi interrompere il ciclo quando l'utente preme il primo tasto o quando l'input è completo? – Wintermute

+0

Fondamentalmente, non voglio che l'input influenzi il programma, in modo che il ciclo sia continuo, ma ogni tanto il sistema ottiene nuovi dati con cui lavorare, sotto forma di un input – Orfby

risposta

1

È triste che non vi sia un modo semplice portatile per verificare in modo asincrono se è stata rilevata una chiave. Ma immagino che il comitato standard abbia attentamente valutato i pro ei contro.

Se non si vuole fare affidamento su librerie di gestione degli eventi di terze parti, e se il multithreading sarebbe un errore, un'alternativa potrebbe essere quella di avere la propria versione di kbhit(), con compilazione condizionale per gli ambienti si desidera supportare:

  • se il tuo conio.h supporta kbhit() basta usarlo.
  • per Windows, è possibile fare riferimento a _kbhit()
  • per Linux e POSIX, è possibile utilizzare la risposta di Matthieu, o cercare here per il codice di Morgan Mattews

Non è la risposta più accademica, ma è pragmatico.

2

Disclaimer: il seguente sembra funzionare con gcc su Linux, tuttavia per alcuni motivi non funziona con VC++ su Windows. Le specifiche sembrano dare un sacco di margine di manovra per le implementazioni di qui, e VC++ prende sicuramente vantaggio da esso ...

Ci sono molteplici funzioni disponibili su qualsiasi std::basic_istream o il suo sottostante std::basic_streambuf.

Per sapere se c'è qualche personaggio a disposizione per l'ingresso, si può chiamare in_avail su std::basic_streambuf:

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

in_avail ti dà il numero di caratteri disponibili, senza bloccare, restituisce -1 se non esiste carattere. In seguito, è possibile utilizzare le normali operazioni di lettura "formattate" come std::cin >> input.

In caso contrario, per non formattato legge, è possibile utilizzare readsome da std::basic_istream, che restituisce fino a N caratteri disponibili senza bloccare:

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

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

Tuttavia si rileva che l'attuazione di questo metodo è molto variabile, quindi per un programma portatile potrebbe non essere così utile.

Nota: come indicato nel commento, l'approccio in_avail potrebbe essere discutibile. I confirm it can work, tuttavia, per prima cosa è necessario utilizzare una funzione oscura dei flussi IO C++: std::ios_base::sync_with_stdio(false) che consente agli stream C++ di eseguire il buffer di input (quindi rubarlo dai buffer stdio di C).

+1

Questo non funziona in modo portabile. Con gcc's libstdC++, 'std :: cin.rdbuf() -> in_avail()' restituisce sempre 0 (a meno che non si passi il proprio buffer di flusso). – Wintermute

+2

Nessuna delle soluzioni funziona su Visual Studio: con 'in_avail()' è sempre true e 'readsome()' restituisce sempre 0. – Christophe

+0

@Wintermute: mi piacerebbe dire "interessante", ma a questo punto non è nemmeno divertente, ma avrei dovuto pensarci. Per 'in_avail' il problema è risolto usando' sync_with_stdio', l'ho aggiunto come risposta. –

2

Potrebbe valere la pena di cercare in multi-threading per questo. Solitamente sono riluttante a suggerirlo, perché il multithreading attira una serie di potenziali problemi che possono risultare difficili da debugare, ma in questo caso possono essere isolati abbastanza facilmente. Mi immagino qualcosa di simile:

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

A prima vista questo sembra un po 'uno spreco perché mantiene la deposizione delle uova e le discussioni buttare via, ma l'input dell'utente prende, in termini, un'eternità di calcolo, quindi l'overhead non dovrebbe essere proibitivo. Ancora più importante, ha il vantaggio di evitare qualsiasi suggerimento di gare di dati all'ingrosso perché l'unico mezzo di comunicazione tra il loop principale (input) e il thread in background è il flag di interruzione atomica e l'applicazione di x ai dati condivisi avviene quando no thread è in esecuzione che potrebbe correre il ciclo principale.

+0

Mentre questa soluzione sembra promettente, secondo alcuni altri punti, gcc non supporta std :: thread. A meno che il mio compilatore non sia impostato correttamente (che è Code :: Blocks nel caso qualcuno possa aiutare) – Orfby

+0

gcc ha supportato 'std :: thread' per un po 'di tempo; L'ho provato qui con gcc 4.9 sotto Linux. Ma se hai bisogno di supportare gcc molto vecchi, c'è sempre [Boost.Thread.] (Http://www.boost.org/doc/libs/1_57_0/doc/html/thread.html) Gli articoli standard per le librerie in gran parte basati su di esso, quindi funzionerà quasi allo stesso modo. EDIT: Forse devi configurare i blocchi di codice per passare a g ++ l'opzione '-std = C++ 11'? – Wintermute

+0

@Ben hai usato l'opzione di compilazione -std = C++ 11? – Christophe