Introduction
In an interactive shell application, you might want to ask the user for value, or let them stick with the default or current value. In such cases, it's quite handy to be able to accept a press on the Enter key, without any preceding data, as a shorthand for keeping the current.
Number of runs (5):
Snoffle variant (pink): red
In the example above, the user has pressed enter without giving a value when prompted for number of runs, and has thus accepted the current setting of 5. In contrast, the user has decided to change the snoffle variant from pink to red. Presumably the user (and the programmer) knows the significance of all this.
The problem is that while the standard input stream std::cin
is good at reading and parsing data, it will read as much as it needs, and no more. This means that for strings, it will read until the first whitespace character, so you only get one word. For ints and floats, it will ignore any initial whitespace characters, like space and newline, until it finds the beginning of a number (or an invalid character), and leave anything after the number, which may lead to there being a newline in the buffer when you come next to read a string.
A generic solution
The solution lies in always reading everything until newline, which is what the standalone function std::getline
is for, into a string, and then, once we have the whole user input, attempt to parse it. By using a std::stringstream
, we'll be relying on the same parsing routines as std::cin
would feed into. Making it a template
function is only natural, since the streams (both std::cin
and std::stringstream
) are designed to work with template types.
#include <iostream>
#include <sstream>
template <typename T>
bool get_user_input(T& val)
{
std::string s;
std::getline( std::cin, s);
if (s.empty())
return false;
std::stringstream ss;
ss << s;
ss >> val;
return true;
}
This is very simple and straigtforward to use:
int runs = get_number_of_runs();
std::cout << "Number of runs (" << runs << "): ";
if (get_user_input(runs))
set_number_of_runs(runs);
std::string snoffle = get_snoffle();
std::cout << "Snoffle variant (" << snoffle << "): "
if (get_user_input(snoffle))
set_snoffle(snoffle);
You'll note that we get the current value, so we can display it, and only replace it if we have been given a new one.