I am implementing a system where the user should feed my program a command. I then split up the string of their input into tokens, delineated by spaces, stored in a vector.
My initial idea was to use a bunch of if-else statements to determine which command should be triggered based on their input. Something like so:
vector<string> userInput;
if (userInput[0] == "help") {
//do something
} else if (userInput[0] == "exit") {
//do something else
} else if (....) {
and so on. But this seems clumsy. Are there better practices for approaching a problem like this? I've already looked into implementing a Command pattern, and even while using it, it appears like I'll run into the same problem of parsing user input to instantiate/execute a specific command.
Thanks in advance!
You can use an std::unordered_map
to store a mapping from the command name to its handler.
Example:
#include <unordered_map>
#include <vector>
#include <string>
// The command handlers.
void help(std::vector<std::string> const&);
void exit(std::vector<std::string> const&);
// CommandHandler is a pointer to a function.
using CommandHandler = void(*)(std::vector<std::string> const&);
// Maps a string to a function pointer.
using CommandHandlers = std::unordered_map<char const*, CommandHandler>;
// Associate command names with handlers.
CommandHandlers const command_handlers{
{"help", help},
{"exit", exit},
{"abort", [](auto&) { std::abort(); }} // Can use a stateless lambda.
};
void handle(std::vector<std::string> const& userInput) {
auto found = command_handlers.find(userInput[0].c_str());
if(found == command_handlers.end())
; // Handle invalid command.
else
found->second(userInput); // Invoke the command handler.
}
A plain array with linear or binary search can be used instead of std::unordered_map
if the number of commands is small.
std::function<void(std::vector<std::string> const&)>
can be used instead of plain function pointer to allow stateful lambdas or member functions as command handlers.