3

I've seen similar posts to this all around google/stackoverflow. However, I can't find one close enough to my scenario and I don't know C/C++ well enough to port other suggestions over to my methods. Perhaps that's a sign in and of itself...

Regardless, here is my code:

while (true)
{
    print("\nSend message, enter command, or \"9\" for help.\n");

    if (cin >> input)
    {
        if (input == TERMINAL_HELP)
        {
            //Some help code.
        }
        else if (input == TERMINAL_EXIT)
        {
            //Some exit code.
        }
        else if (input < 4 && input >= 0)
        {
            // Some processing code.
        }
        else
        {
            print("Please enter a valid message.");
        }
    }
    else
    {
        print("Please enter a valid message.");
    }
}

The catches works fine for single characters or integers outside of the range [0-4]. But when I put a string in, it gets very weird. It keeps looping through itself infinitely. However after the first time this should be impossible because I do not press enter. It continues through as if it is receiving a blank infinitely.

If you have any suggestions or can point me in the direction of fixing my issue I'd appreciate it! Thanks!

NOTE:

I was trying to follow this, and it worked to some extent. (I got the cin >> input within the if statement idea from this link...) But it does not work to block strings from making the program loop oddly.

Good input validation loop using cin - C++

Community
  • 1
  • 1
eatonphil
  • 13,115
  • 27
  • 76
  • 133
  • 4
    There *are* hundreds of duplicates of this, you just have to read carefully through the explanations. Or just read the manual for iostreams. – Kerrek SB Jul 10 '13 at 18:47
  • 1
    So this is probably not worth leaving up? – eatonphil Jul 10 '13 at 18:48
  • See how it pans out. Maybe you'll get a better response here than any of the existing ones... (but do also search this site and read language referencs to see if you can figure it out yourself). A good Baby's First Test of your own understanding is to ask yourself what happens if the input file is closed, as in `echo "" | ./a.out`. – Kerrek SB Jul 10 '13 at 18:49
  • Sounds good. I will. I posted a stack question I was using to make some amends to my code above. As I said, it was helpful, but didn't stop my issue with strings. – eatonphil Jul 10 '13 at 18:51
  • 1
    @MooingDuck. I referenced that link in my post. I also explained why that question does not answer mine. – eatonphil Jul 10 '13 at 19:12
  • @phileaton: You explained, but you were wrong. The code in the first answer does indeed answer this question. Namely, look at what he does in the else block of `if(cin>>input)` compared to what you do. – Mooing Duck Jul 10 '13 at 19:14

2 Answers2

7

Just to get you started with something..

Key points:

  • the iostreams (including cin) have something called "error flags"
  • when error occurs, the stream can be configured to either *) raise an exception *) skip next operations
  • the default configuration is .. to not throw and skip further operations

This means, that if you do:

cin >> integer1;
cin >> integer2; // *!
cin >> integer3; // !
cin >> integer4; // !

and if the user provides a non-integer at the line marked with (*), then at this point of time the cin will enter an error state and operations marked with (!) will be skipped.

Most probably this is why you get weird behavior like infinite read loops etc.

To handle this:

  • either configure the stream to throw exceptions - and catch them
  • or check error state after every few reads - and handle them

i.e.

cin >> integer1; if(cin.fail()) { cout << "wtf?!"; cin.clear(); .. return; }
cin >> integer2; if(cin.fail()) { cout << "wtf?!"; cin.clear(); .. return; }
cin >> integer3; if(cin.fail()) { cout << "wtf?!"; cin.clear(); .. return; }
cin >> integer4; if(cin.fail()) { cout << "wtf?!"; cin.clear(); .. return; }

cin.reset clears error flags and allows further operation on the stream. However, all the trash-data will still linger in the stream. So, there's no point in trying to read further. After cin.reset you should somehow remove that bad-data and recover from the situation. I've marked the places with "..", because there are many ways to do it.

For example, cin.ignore..

Now please refer to all-that-other posts :) I'm sure that you will now quickly find a solution

EDIT: aargh.. sorry, I've floated too far from the actual answer.. What you actually wanted is not a good-errorhandling, but something similar to what sehe wrote: instead of reading an integers, you should read a "string" and then inspect it, and then either re-parse them as integers or treat as string-data..

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • You're thinking `cin.clear()`, not `reset()` (that doesn't exist). You'll also want to ignore some of the input. And no need to be that verbose, just say `if (!(cin >> integer1)) { cout << "wtf"; }`. – jrok Jul 10 '13 at 19:11
  • as for the ! versus fail(), there were some issues with that.. I dont remeber precisely, but it was related to the `void* operator` being equivalent to `good` which was not equal to `!fail` and something with `eof` handling... maybe it was changed in C11, I dont remember now really.. – quetzalcoatl Jul 10 '13 at 19:14
2

Here is a slightly fixed up minimal example (that compiles...) showing how you could do the stuff you try around

 msg = input + '0';

Namely:

std::ostringstream oss;
oss << input;
auto msg = oss.str();

Or, using even more c++11 features (if your compiler has them):

auto msg = to_string(input);

The rest without further comments:

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int const TERMINAL_EXIT = 9;
int const TERMINAL_HELP = 8;

static int devHandle = 0;

void print(char const*const  msg) { cout << msg; }

void send(int, std::string const&) {} // TODO
void receive(int, std::string&)    {} // TODO
void help_routine() {} // TODO

int main(int argc, const char *argv[])
{
    while (true)
    {
        print("\nSend message, enter command, or \"9\" for help.\n");
        // Read in message to send to DigiSpark.

        long input;
        if (cin >> input)
        {
            if (input == TERMINAL_HELP)
            {
                help_routine();
            }
            else if (input == TERMINAL_EXIT)
            {
                break;
            }
            else if (input < 4 && input >= 0)
            {
#ifdef HAVE_CXX11
                std::string msg = to_string(input);
#else
                std::ostringstream oss;
                oss << input;
                std::string msg = oss.str();
#endif
                // Send mimicking command line arguments.
                send(devHandle, msg);
                cout << "\nSent message: " << msg << endl;
                print("Receiving message...\n");

                receive(devHandle, msg);

                while (!msg.empty())
                {
                    receive(devHandle, msg);
                }

                cout << "Received message: " << msg << endl;
            }
            else
            {
                print("Please enter a valid message.");
            }
        }
        else
        {
            print("Please enter a valid message.");
        }
    }

    return 0;
}
sehe
  • 374,641
  • 47
  • 450
  • 633