Dev Notes

Software Development Resources by David Egan.

Define User Input From an Integer List in C++


C++
David Egan

There are many ways to collect user input. In this example a user is prompted to make a selection from a numbered list. This sets the int choice variable, which can then be used to control programme flow.

The possible values for user selection, along with a related string label for each value are set up using a struct.

Input is handled by std::getline(), with the input converted to an integer by means of the std::stringstream operator >>. I’ve done it this way rather than using std::cin so that an empty return can be handled and interpreted as the default value, if this has been set in the selectAction() function call.

Example Code

Compile this using c++11 standards. for example: g++ -o test -std=c++11 test.cpp

#include <iostream>
#include <algorithm> // Needed for any_of()
#include <string>
#include <map>
#include <sstream>

int selectAction(int defaultChoice);

int main(int argc, char const *argv[])
{
    int userChoice = 0;
    std::cout << "This program prompts a user for input." << std::endl;
    // Pass in a default value 1
    userChoice = selectAction(1);
    std::cout << "Choice made: " << userChoice << std::endl;
    return 0;
}

int selectAction(int defaultChoice)
{
    // Create a data structure `optionPair` so that values may be associated with
    // labels, and initialize an array of `optionPair` elements.
    struct optionPair {
        std::string label;
        int value;
    } options[] = {
        {"Label for Option 1", 1},
        {"Label for Option 2", 2},
        {"Label for Option 3", 3}
    };
    // Create an array of valid input values
    const int nOptions = sizeof(options)/sizeof(optionPair);
    int validChoices[nOptions];
    for (int i = 0; i < nOptions; i++) {
        validChoices[i] = options[i].value;
    }
    int choice;
    bool validChoice;
    while (true) {
        int choice = 0;
        std::string input = "";
        std::cout << "Please select an option:" << std::endl;
        for (auto&& option : options)
        {
            std::cout << option.value << ") " << option.label << std::endl;
        }
        std::getline(std::cin, input);
        std::stringstream tempStream(input);
        // Is it empty? User just hit return
        if (tempStream.str().empty()) {
            choice = -1;
        } else {
            // The user entered something, so convert it to an integer. If the
            // entry is not a number, it will be transformed to 0.
            tempStream >> choice;
        }

        // `[&]` specifies that the lambda uses variable capture - needed here
        // since the lambda accesses the `choice` variable.
        validChoice = std::any_of(std::begin(validChoices), std::end(validChoices), [&](const int& i)
        {
            return i == choice;
        });

        if (defaultChoice && choice == -1) {
            // User entered return & a default exists - return the default
            return defaultChoice;
        } else if (validChoice) {
            // User selection returned if valid
            return choice;
        }

        // User feedback for invalid entry
        std::cout << "You've entered an invalid selection." << std::endl
        << "Please enter a number from the specified list. Thanks." << std::endl;
    }
}

I’m sure there are better ways to allow users to choose from a list of integer values - please leave a comment if you have a suggestion.

References


comments powered by Disqus