Read user input or Enter

Code

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

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 ttemplate types.

#include <iostream>
#include <sstream>

/*! Read user input until Enter is pressed. 
    \tparam T type of data to read
    \param val will hold the given input, if any
    \return false if Enter was pressed at once, true if data was given
*/
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.

Advertisements

2 thoughts on “Read user input or Enter

  1. Hm, I think I would, personally, re-do it as a “get_defaulted_user_input”, taking a prompt, a pointer to the variable and (optionally) a stream to get the input from, then mutate the variable if we got some input. Maybe.

    That does potentially play merry havoc with the bizarre fetish of using accessors, though…

    1. Ah, but the snoffle handling may well be in a closed 3d party library, forcing us to use the accessor. Or perhaps the accessor is there to convert strings like “pink” or “red” into an enumeration of allowed hues, which is what is used internally (this would certainly be more efficient, but it would be less userfriendly to use the numbers in the UI).

      But for the cases where a variable can be manipulated directly, yes, it would be handy to have

      template
      void get_defaulted_user_input(const std::string& prompt, T& val)

      as well. 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s