0

I make a program which will have some functions to work with my structure. I haven't added the functions yet, but I have problems with user interface. When I input "hello" instead of int (for choice & choice2), I expect to get exception but instead I get infinite loop. If "hello" is an array of chars and chars are transferred to corresponding ASCII codes, I don't understand why loop is infinite. I need exception so I can catch it.

#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
#define MARK_AMOUNT 4

struct student{
    string name;
    string group;
    int mark[MARK_AMOUNT];
};

istream& operator >> (istream& in, student& student) {
    in >> student.name >> student.group;
    for (int i = 0; i < MARK_AMOUNT; i++) in >> student.mark[i];
    return in;
}

ostream& operator << (ostream& out, const student& student) {
    out << student.name << "\t" << student.group << "\t";
    for (int i = 0; i < MARK_AMOUNT; i++) out << student.mark[i] << " ";
    return out;
}

int main(){
    vector <student> list;
    int choice, choice2;
    ifstream myfile;
    bool input = false, input2 = false;
    while(!input){
        cout << "Input:\n1 to work with files\n2 to work in console\n";
        cin >> choice;
        if(choice == 1 || choice == 2) input = true;
        else cout << "Wrong number.\n";
    }
    while(!input2){
        cout << "Input function number[1-3]: ";
        cin >> choice2;
        if(choice2 >= 1 && choice2 <= 3) input = true;
        else cout << "Wrong number.\n";
    }
    switch (choice) {
        case 1:
        {
            string fileName;
            cout << "Input name of file: ";
            cin >> fileName;
            ifstream myfile(fileName);
            student temp;
            while(myfile >> temp){
                list.push_back(temp);
            }
        }
            break;
        case 2:
            break;
        default:
            break;
    }
    switch (choice2) {
        case 1:
            break;
        case 2:
            break;
        case 3:
            break;
        default:
            break;
    }
    switch (choice) {
        case 1:
        {
            myfile.close();
            break;
        }
        case 2:
            break;
        default:
            break;
    }
    return 0;
}

Output:

Input:
1 to work with files
2 to work in console
hello
Wrong number.
Input:
1 to work with files
2 to work in console
Wrong number.
Input:
1 to work with files
2 to work in console
Wrong number.
...

...
Martian
  • 227
  • 1
  • 15
  • 4
    [std::cin doesn't throw an exception on bad input](https://stackoverflow.com/questions/26187729/stdcin-doesnt-throw-an-exception-on-bad-input). TL;DR - it isn't supposed to throw anything (unless you ask nicely to start throwing). And while `hello` indeed can be converted to series of ints, it's almost never what you want, so `std::cin` extraction fails instead. – Yksisarvinen Apr 26 '20 at 14:50
  • You can create your own class that inherits Exception class. But for this example, something simpler would suffice: improve while condition to check if choice for a specific set of chars. – Gotiasits Apr 26 '20 at 15:05
  • @Yksisarvinen I'm not sure where I should add if in my while cycle – Martian Apr 26 '20 at 15:08
  • @Gotiasits I could check if choice is not a number and just break then, but I don't want break, I want user to be able to type it again. Maybe I should use some temp variable, to check it and then assign it to choice. Also, can you tell more about using classes here? I don't understand how could it work. Special class for choice variables? – Martian Apr 26 '20 at 15:17

1 Answers1

0

If you want execution to continue then you definitely do not need an exception. You need a mechanism to first check if the input is valid since std::cin goes into an error state and leaves the input in its buffer, thus skipping through the next call.

Here is the sample on wich you can build upon:

#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main(){

    int choice, choice2;   
    bool input = false, input2 = false;

    std::string line;

    while(!input){

        cout << "Input:\n1 to work with files\n2 to work in console" << endl;
        while (std::getline(std::cin, line))
            {

                std::stringstream ss(line);
                if (ss >> choice)
                {
                    if (ss.eof())
                    {   // Success
                        break;
                    }
                }
                std::cout << "Invalid Input, try again" << endl;
            }

        if(choice == 1 || choice == 2) input = true;
        else {
            cout << "Wrong number.\n" << endl;

        }
    }
}
Gotiasits
  • 1,135
  • 1
  • 10
  • 21
  • What do you use endl after \n for? I know that endl is \n + stream flush, do we need this flush there? – Martian Apr 26 '20 at 17:39
  • In this particular case, its a typo, however its generally a good idea to use `::endl` instead of \n when it is possible. Nice catch, will remove it... – Gotiasits Apr 26 '20 at 17:48
  • Isn't it excessive, making program slower? – Martian Apr 26 '20 at 18:12
  • "Premature optimization is the root of all evil" Also, that would be separate question that I'm quite surre is discuesed on zilion places... – Gotiasits Apr 26 '20 at 18:14
  • Haha yes I remember I searched it already but nobody convinced me to use one over another. – Martian Apr 26 '20 at 18:48