It would be a good coding practice to create a class for each command and inherit those classes from a common base class with a virtual function taking a vector of arguments. In your case the arguments are strings, so the command processing methods can take a vector of strings as arguments and return e.g. program exit code. Then comes a map, more specifically a hash table which is unordered_map in C++ because ordered iteration doesn't seem needed here. In that unordered_map the keys are lowercase command names and values are pointers to the instance of the class processing that command. The source code example is below:
#include <unordered_map>
#include <string>
#include <cstdint>
#include <vector>
#include <iostream>
#include <memory>
enum class ExitCode : int32_t
{
OK = 0,
WRONG_USAGE = 1,
// Change the values below to your specific error (exit) codes
SOME_ERROR = 2,
OTHER_ERROR = 3
};
class CommandProcessor
{
public:
virtual ExitCode Go(const std::vector<std::string>& parameters) = 0;
};
class FooCommandProcessor : public CommandProcessor
{
public:
virtual ExitCode Go(const std::vector<std::string>& parameters) override
{
// Implement processing of Foo command here
return ExitCode::OK;
}
};
class BarCommandProcessor : public CommandProcessor
{
virtual ExitCode Go(const std::vector<std::string>& parameters) override
{
// Implement processing of Bar command here
return ExitCode::OK;
}
};
// Implement classes processing the other commands here
class CommandSelector
{
typedef std::unordered_map<std::string, std::shared_ptr<CommandProcessor>>
StringCommandProcessorMap;
StringCommandProcessorMap _scpm;
template <class CP> void RegisterCommand(const std::string& command)
{
_scpm.insert(StringCommandProcessorMap::value_type(
command, std::shared_ptr<CommandProcessor>(new CP())));
}
public:
CommandSelector()
{
RegisterCommand<FooCommandProcessor>("foo");
RegisterCommand<BarCommandProcessor>("bar");
// Register the rest of your commands here
}
ExitCode InvokeCommand(const std::string& command,
const std::vector<std::string>& parameters)
{
std::string lowercaseCommand;
for (int i = 0; i < int(command.size()); i++)
{
lowercaseCommand.push_back(::tolower(command[i]));
}
StringCommandProcessorMap::iterator it = _scpm.find(lowercaseCommand);
if (it == _scpm.end())
{
std::cout << "Unknown command: " << lowercaseCommand << std::endl;
return ExitCode::WRONG_USAGE;
}
return it->second->Go(parameters);
}
};
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cout << "Usage: <your_exe_name> <command> [arguments]" << std::endl;
return int(ExitCode::WRONG_USAGE);
}
std::string command(argv[1]);
std::vector<std::string> parameters;
for (int i = 2; i < argc; i++)
{
parameters.push_back(std::string(argv[i]));
}
CommandSelector cs;
ExitCode ec = cs.InvokeCommand(command, parameters);
return int(ec);
}