Robust menu-driven programs and input validation
Build reliable console menus with line-based input, parsing, validation, and recovery from malformed data instead of getting stuck in failed input streams.
Prerequisites and outcomes
Know cin, loops, functions, and strings. You will learn why formatted extraction can fail, how to recover safely, and how to separate reading, parsing, validation, and application logic.
Why console input fails
Formatted extraction such as cin >> number is convenient, but if the user types text where an integer is expected, the stream enters a failed state. Later reads fail until the state is cleared and invalid input is removed. Mixing >> with getline can also leave a newline waiting to be consumed.
Prefer line-based input at the boundary
#include <iostream>
#include <string>
int main() {
std::string line;
std::cout << "Enter your name: ";
if (!std::getline(std::cin, line)) return 1;
std::cout << "Hello, " << line << '\n';
}Read one complete line, then parse it. This avoids leaving part of a line in the stream and gives the program control over malformed input.
Parse an integer with from_chars
#include <charconv>
#include <string>
#include <system_error>
bool parse_int(const std::string& text, int& value) {
const char* first = text.data();
const char* last = first + text.size();
auto result = std::from_chars(first, last, value);
return result.ec == std::errc{} && result.ptr == last;
}std::from_chars is available for integers from C++17. Checking both the error code and the returned pointer rejects overflow and trailing junk such as 12abc. Empty strings and whitespace need an explicit policy; this example accepts only a plain integer with no surrounding spaces.
Menu loop with clear control flow
for (;;) {
std::cout << "1. Add\n2. List\n0. Exit\nChoice: ";
std::string line;
if (!std::getline(std::cin, line)) break; // EOF or input failure
int choice{};
if (!parse_int(line, choice)) { std::cout << "Enter a whole number.\n"; continue; }
if (choice == 0) break;
if (choice == 1) { /* call add operation */ }
else if (choice == 2) { /* call list operation */ }
else std::cout << "Unknown choice.\n";
}Keep the menu responsible for interaction only. Put business rules in separate functions so they can be tested without typing into the console.
Validation has layers
- Syntax: can the text be parsed as an integer?
- Range: is the number within allowed bounds?
- Meaning: is this choice valid in the current application state?
- Persistence: can the change be saved successfully?
Common mistakes
- Clearing a stream but not discarding the rest of the invalid line.
- Using
ignore()with a small fixed count and assuming every line is shorter. - Accepting a valid numeric prefix while silently ignoring trailing characters.
- Confusing EOF with a user's invalid menu choice.
- Putting validation only in the menu so other callers can bypass it.
Practice
- Why does checking
result.ptr == lastmatter? - What should a menu do when
getlinereaches EOF? - Where should a rule such as “quantity must be positive” live?
Show reasoned answers
- It ensures the entire line was parsed, not just a valid prefix.
- Exit or return cleanly; do not loop forever.
- In the operation or domain layer, so every caller follows the same rule.
Summary and next step
Read complete lines, parse explicitly, validate syntax/range/meaning, and handle EOF separately. Keep user interaction separate from business rules. Next, apply these ideas in a complete multi-file capstone.