2013-05-16 33 views
10

Obecnie mam program, który czyta ze standardowego wejścia, czasami program musi po prostu działać, jeśli nie wprowadzono żadnych danych, zwykle jest to skrypt testowy, nie ma "wprowadź", że tak powiem.non-blocking std :: getline, zakończ jeśli nie wprowadzono

program -v1 -v2 -v3 <input >output

v1 - v3 są argumenty linii poleceń odpowiednio

Zasadniczo program wypluwa argumenty wiersza polecenia i ich odpowiednie znaczenie programu, jeżeli nie „wejście” jest podana, a następnie powinien wyjść.

Jednak w tej chwili, jeśli nadaj mu pusty plik testowy lub po prostu uruchom bez naciśnięcia klawisza Enter po uruchomieniu bloków na std :: getline używam do wprowadzania poleceń.

while(std::getline(std::cin,foo) 
{do stuff} 

gdzie foo jest ciągiem.

Co najmniej jeden raz uruchomić, a następnie do stuff, a następnie wyjść w przypadku braku danych wejściowych? W przypadku wprowadzenia, do stuff występuje raz dla każdej linii standardowego wejścia.

Czy można przełączyć się na pętlę do-while z pętlą sprawdzającą, czy ma jakieś dane wejściowe?

Coś

if cin empty 
set flag 

do 
{do stuff 
check flag} 
while(getline) 

lub jest non-blocking IO nie jest możliwe w C++?

To pytanie wydaje się być odświeżane w kółko, ale nie mogłem znaleźć ostatecznej odpowiedzi, a nawet odpowiedzi, która była niezależna od platformy (ten program ma charakter akademicki, jest kodowany w oknach i testowany na systemie UNIX).

+0

czy mówisz, że chcesz uruchomić pętlę raz bez względu na wszystko, a następnie wyjść, jeśli przed wywołaniem getline nie zostanie podane żadne dane wejściowe? –

+0

możliwy duplikat [sprawdzanie dostępności danych przed wywołaniem std :: getline] (http://stackoverflow.com/questions/3317740/checking-data-availability-before-calling-stdgetline) Niestety, prawdopodobnie nie ma przenośnego sposobu zrobić to w standardowym C++. – jrok

+0

Czy możesz użyć niektórych funkcji niskiego poziomu z C? – Zaffy

Odpowiedz

1

Możesz użyć cin.peek, aby sprawdzić, czy jest cokolwiek do czytania, a następnie zadzwoń pod numer getline, jeśli jest. Nie ma jednak czegoś takiego jak samo-blokowanie getline.

+3

To też się zablokuje, jeśli nie będzie dostępnych danych wejściowych. – jrok

+0

jak wyżej również bloków cin.peek, próbowałem. –

+0

'cin.peek' powinien blokować, ponieważ zgodnie ze standardem: _Returns: traits :: eof() if good() jest fałszywe. W przeciwnym razie zwraca rdbuf() -> sgetc() _ O ile rozumiem, nie powinno być możliwości użycia 'peek()' do sprawdzenia strumienia w sposób odblokowujący. –

5

Używanie std :: cin asynchronously może być jedynym sposobem, aby to zadziałało, ponieważ program iostream nie został zaprojektowany tak, aby nie blokował zachowań. Oto przykład:

Async Example 1 Async Example 2 (printing a space out every 1/10th of a second while accepting CLI input at the same time)

Kod jest skomentował tak powinno być łatwe do zrozumienia. Jest to klasa bezpieczna dla wątków, która pozwala asynchronicznie uzyskać linię używając std :: cin.

Bardzo łatwy w użyciu do asynchronicznych celów CLI, z 0% wykorzystaniem procesora na moim komputerze. Działa doskonale na Windows 10 w trybie Visual Studio 2015 C++ Win32 Console Debug and Release. Jeśli nie działa w twoim systemie operacyjnym lub środowisku, to szkoda.

#include <iostream> 
#include <string> 
#include <thread> 
#include <mutex> 
#include <atomic> 

using namespace std; 

//This code works perfectly well on Windows 10 in Visual Studio 2015 c++ Win32 Console Debug and Release mode. 
//If it doesn't work in your OS or environment, that's too bad; guess you'll have to fix it. :(
//You are free to use this code however you please, with one exception: no plagiarism! 
//(You can include this in a much bigger project without giving any credit.) 
//Created 02-15-17 by Andrew Davis, the creator of a new programming language called Basik. 
//If you like this code, please check it out, thanks! http://thecodingwebsite.com 
class AsyncGetline 
{ 
    public: 
     //AsyncGetline is a class that allows for asynchronous CLI getline-style input 
     //(with 0% CPU usage!), which normal iostream usage does not easily allow. 
     AsyncGetline() 
     { 
      input = ""; 
      sendOverNextLine = true; 
      continueGettingInput = true; 

      //Start a new detached thread to call getline over and over again and retrieve new input to be processed. 
      thread([&]() 
      { 
       //Non-synchronized string of input for the getline calls. 
       string synchronousInput; 
       char nextCharacter; 

       //Get the asynchronous input lines. 
       do 
       { 
        //Start with an empty line. 
        synchronousInput = ""; 

        //Process input characters one at a time asynchronously, until a new line character is reached. 
        while (continueGettingInput) 
        { 
         //See if there are any input characters available (asynchronously). 
         while (cin.peek() == EOF) 
         { 
          //Ensure that the other thread is always yielded to when necessary. Don't sleep here; 
          //only yield, in order to ensure that processing will be as responsive as possible. 
          this_thread::yield(); 
         } 

         //Get the next character that is known to be available. 
         nextCharacter = cin.get(); 

         //Check for new line character. 
         if (nextCharacter == '\n') 
         { 
          break; 
         } 

         //Since this character is not a new line character, add it to the synchronousInput string. 
         synchronousInput += nextCharacter; 
        } 

        //Be ready to stop retrieving input at any moment. 
        if (!continueGettingInput) 
        { 
         break; 
        } 

        //Wait until the processing thread is ready to process the next line. 
        while (continueGettingInput && !sendOverNextLine) 
        { 
         //Ensure that the other thread is always yielded to when necessary. Don't sleep here; 
         //only yield, in order to ensure that the processing will be as responsive as possible. 
         this_thread::yield(); 
        } 

        //Be ready to stop retrieving input at any moment. 
        if (!continueGettingInput) 
        { 
         break; 
        } 

        //Safely send the next line of input over for usage in the processing thread. 
        inputLock.lock(); 
        input = synchronousInput; 
        inputLock.unlock(); 

        //Signal that although this thread will read in the next line, 
        //it will not send it over until the processing thread is ready. 
        sendOverNextLine = false; 
       } 
       while (continueGettingInput && input != "exit"); 
      }).detach(); 
     } 

     //Stop getting asynchronous CLI input. 
     ~AsyncGetline() 
     { 
      //Stop the getline thread. 
      continueGettingInput = false; 
     } 

     //Get the next line of input if there is any; if not, sleep for a millisecond and return an empty string. 
     string GetLine() 
     { 
      //See if the next line of input, if any, is ready to be processed. 
      if (sendOverNextLine) 
      { 
       //Don't consume the CPU while waiting for input; this_thread::yield() 
       //would still consume a lot of CPU, so sleep must be used. 
       this_thread::sleep_for(chrono::milliseconds(1)); 

       return ""; 
      } 
      else 
      { 
       //Retrieve the next line of input from the getline thread and store it for return. 
       inputLock.lock(); 
       string returnInput = input; 
       inputLock.unlock(); 

       //Also, signal to the getline thread that it can continue 
       //sending over the next line of input, if available. 
       sendOverNextLine = true; 

       return returnInput; 
      } 
     } 

    private: 
     //Cross-thread-safe boolean to tell the getline thread to stop when AsyncGetline is deconstructed. 
     atomic<bool> continueGettingInput; 

     //Cross-thread-safe boolean to denote when the processing thread is ready for the next input line. 
     //This exists to prevent any previous line(s) from being overwritten by new input lines without 
     //using a queue by only processing further getline input when the processing thread is ready. 
     atomic<bool> sendOverNextLine; 

     //Mutex lock to ensure only one thread (processing vs. getline) is accessing the input string at a time. 
     mutex inputLock; 

     //string utilized safely by each thread due to the inputLock mutex. 
     string input; 
}; 

void main() 
{ 
    AsyncGetline ag; 
    string input; 

    while (true) 
    { 
     //Asynchronously get the next line of input, if any. This function automagically 
     //sleeps a millisecond if there is no getline input. 
     input = ag.GetLine(); 

     //Check to see if there was any input. 
     if (!input.empty()) 
     { 
      //Print out the user's input to demonstrate it being processed. 
      cout << "{" << input << "}\n"; 

      //Check for the exit condition. 
      if (input == "exit") 
      { 
       break; 
      } 
     } 

     //Print out a space character every so often to demonstrate asynchronicity. 
     //cout << " "; 
     //this_thread::sleep_for(chrono::milliseconds(100)); 
    } 

    cout << "\n\n"; 
    system("pause"); 
} 
+3

Hej, włożyłem w to dużo pracy, działa dobrze i ma bardzo dobre komentarze. Dlaczego w dół? – Andrew

+2

Nie jestem pewien, dlaczego to zostało odrzucone, ale tutaj jest przegłosowany! –