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

Removing whitespace

Code, CodeProject

Here’s a std::string, please remove all whitespace from it. How would you do it? Despite its seeming simplicity, it’s an interesting question, because it can be done in so many ways.

To start with, how do you identify whitespace? Let’s have a look at some different approaches (all of which I’ve seen in the wild):

// Simple
bool iswhitespace1(char c)
{
  // Is it  space   or    tab      or    return   or    newline?
  return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n');
}
// Cute attempt at cleverness
bool iswhitespace2(char c)
{
  // Is it one of the whitespace characters?
  static const std::string spaces(" \t\r\n");
  return (std::string::npos != spaces.find(c));
}
// Probably ok, for English at least
bool iswhitespace3(char c)
{
  // Using C function, from <cctype>
  return ::isspace(c);
}
// As above, but standard C++ instead of standard C
bool iswhitespace4(char c)
{
  // Using current locale, and std function from <locale>
  static const std::locale loc;
  return std::isspace(c, loc);
}

If we were to run through these four functions with values of c from 0 to 255, the first two would produce the same result, and the latter two would (probably) produce the same result, but those wouldn’t be the same as for the first two.