Search code examples
c++cingetline

Ways std::getline(std::cin, string) can fail from keyboard input


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.


Solution

  • 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).