khajvah
khajvah

Reputation: 5090

Clearing input stream

I know this is too famous question but I cannot seem to find an answer

I have smth like this:

 while (true) {

        std::cout << "\nCommand> ";
        std::cin.get(input, MAX, '\n');

        std::string cmdtemp = input;

        std::string token;
        std::istringstream issc(cmdtemp);

        std::string cmd[MAX];
        while ( getline(issc, token, ' ') )
        {
            cmd[b] = token;
            b++;
        }


        if (cmd[0] == "help") {
            Help cmd(cmd);
            std::cin.ignore(100, '\n') ;

        }
        else if (cmd[0] == "modify")
            Command* cmd = new Modify;
std::cin.ignore(100, '\n') ;
        else if (cmd[0] == "convert")
            Command* cmd = new Convert;
std::cin.ignore(100, '\n') ;
        else if (cmd[0] == "show")
            Command* cmd = new Show;
std::cin.ignore(100, '\n') ;
        else if (cmd[0] == "getrates") 
            Command* cmd = new Getrates;
std::cin.ignore(100, '\n') ;
        else {
            std::cout << "Wrong command. Type help for Help.";
std::cin.ignore(100, '\n') ;        
}

After second input, this becomes infinite loop(though the stream should be cleared even after 2nd input, am I right?), also, if I, for example, enter "help" two times, first one works but the second one doesn't

What can I do to get user input correctly as many times as I wish?

EDIT: When user enters one of the commands(if else...), an object is created and constructor runs. In case of "help" the constructor displays command list with explanations. This works only once in my case. When I enter "help", the constructor works but when I enter "help" once more, it displays infinitely many "wrong command" (else condition in my if else). That is because of stream I guess but I though cin.ignore(...) should clear and the code should work. Hope this explanation is enough :)

Thanks in advance:)

Upvotes: 0

Views: 588

Answers (1)

cHao
cHao

Reputation: 86525

I notice that b is not defined within the loop. Unless it's a global and all of the command classes reset it, it's never getting set back to 0...which means that first "Help" will always be "Help". Worse still, eventually you're going to write past your array.

Since b is pretty much entirely an index into cmd and a count of words, it should probably be defined in the same scope. Both problems can be kind-of-fixed by adding b = 0; right before the loop that fills cmd. (If you don't use b outside of the loop, though, you should really be defining it inside.)

But really, you'd probably do better to use std::getline to read a whole line at once as a std::string, and then use istringstream's extraction operator (rather than get()) to extract the individual words into a vector. (char input[MAX]; is what you do in C. C++ has dynamically resizable containers built in, so there's not much point in the array. It'll invariably either be too big or too small.)

Watch:

while (true) {
    std::cout << "Command>> " << std::flush;
    std::string line;
    std::getline(std::cin, line);

    std::istringstream tokenizer (line);
    std::vector<std::string> cmd;

    // this basically just does `tokenizer >>` and sticks the string into cmd
    // til the stream runs out.
    typedef std::istream_iterator<std::string> words;
    std::copy(words(tokenizer), words(), std::back_inserter(cmd));

    if (cmd.empty()) continue;

    // do stuff with `cmd`, which is a vector of words.  For example:
    if (cmd[0] == "quit") break;
    for (std::string word : cmd) {
         std::cout << word << '\n';
    }
}

Note that there's no buffer to overrun, and no artificial limitation on line size.

Upvotes: 3

Related Questions