3
votes

I am writing this function to ask for a particular input type. is_type just validates that the string recieved can be casted using stringstream to the desired type.

template<typename T>
T get_type(std::string prompt)
{
    T output;
    std::cout << prompt;
    std::string Input;
    while (std::getline(std::cin, Input) && !is_type<T>(Input))
    {
            std::cout << "Invalid input type. Please try again:\n"
              << prompt;
    }

    std::stringstream(Input) >> output;
       return output;
}

The functions seems to work as desired except when I type ctrl + Z for example. What is the appropriate way to deal with this?

I added:

template<typename T>
    T get_type(std::string prompt)
    {
        T output;
        std::cout << prompt;
        std::string Input;
        while (std::getline(std::cin, Input) && !is_type<T>(Input))
        {
                std::cout << "Invalid input type. Please try again:\n"
              << prompt;
        }
        if (!std::cin)
        {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        output = get_type<std::string>(prompt) ;
        return output;
        }
        std::stringstream(Input) >> output;
           return output;
    }

Which asks again for input after for example ctrl+Z Does that solve my problem of std::getline(std::cin, std::string) failing under kewyboard input from the user?

Also, why do I have to hit enter 2 times for the

output = get_type<std::string>(prompt) ; 

line to run inside the if.

1
Your function returns a value. If it can't read a line from stdin, and can't return a meaningful default value, I think the only appropriate thing to do is raise an exception.Remy Lebeau
Also, your is_type seems redundant, since you can simply check the result of stringstream::operator>> inside the loop, instead of after the loop endsRemy Lebeau
if it can't get a value (because input is eof) I managed to keep asking using the second code. But is that safe? In what other ways can keyboard input fail under getline(cin, string)Daniel Duque
What does your initial code do on ^Z that you don’t want?Davis Herring

1 Answers

0
votes

std::getline could fail if you've used stdin previously without clearing the failbit and if the input exceeds std::string::max_size (see comment of Davis Herring). Otherwise, I know of no way to let std::getline fail except by EOF (^Z/^D).

But, here is your code with some small improvements:

template<typename T>
T get_type(std::string prompt)
{
    T output;
    std::string input;
    while(true)
    {
        std::cout << prompt;
        std::getline(std::cin, input);
        std::istringstream iss(input);
        if(!std::cin)
        {
            std::cin.clear();
        //  std::clearerr(stdin);
        }
        else if(iss >> output && iss.eof())
            return output;

        std::cout << "Invalid input type. Please try again:\n";
    }
}

As mentioned in the comments, it is necessary to use clearerr on stdin on some systems. If your system requires that, just uncomment std::clearerr(stdin);.

Because of your 2x <Enter> problem: the ignore statement is unnecessary. You just ignore the next input (that's why you have to hit <Enter> twice).