C ++ - call input function as string
I am new to C ++ and I am writing a program that executes certain commands. My program should have about 200 commands and use strcmp
to check if a line is one of the commands seems to be slow and inaccurate to me. I am wondering if there is a function that could call a given input directly as a command.
For example:
void main() {
char test[60], param[10];
std::cin >> test >> param;
callFunction(test, param);
}
NOTE. I've already searched and found a way to use maps, but what if the number of arguments for each function is different? Any help would be appreciated, thanks!
source to share
It would be 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 command methods can take a vector of strings as arguments and return eg. program exit code. Then there is a map, or rather a hash table, which is an unordered_map in C ++ because ordered iteration is not needed here. In this unordered_map, the keys are lowercase command names, and the values ββare pointers to an instance of the command to process that command. An example source code is given 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);
}
source to share
You can call methods exec
with the required argument to run your commands
Checkout: http://linux.die.net/man/3/exec
source to share
Check the command pattern:
Encapsulate all commands / functions in their own objects. Chances are you don't want 200 different command classes, but just a few, a grouping of similar function calls with the same target and count and type argument.
Then create a string map for these command objects. All command objects have the same interface, and the differences in the number of arguments and the type of the original fancripts are encapsulated internally.
source to share
Function call table (or similar). If speed is important, use std::unordered_map
to do something like:
std::unordered_map<std::string, function> cmds;
...
cmds["mycommand"] = myCommandFunction();
I personally wrote a dozen different programs with static array tables with a string + function pointer and just used a simple loop to iterate over the array - usually not the slowest part of the design [if speed matters, profile your code to see where it takes time. and then optimize, but start by writing clear and simple code, don't make the code more complicated simply because you think it might be most of the time before you measured it]
An example of usage std::map
and function pointers [in this case lambda functions] can be found here:
https://github.com/Leporacanthicus/lacsap/blob/master/builtin.cpp#L972
source to share
An example of my comment:
#include <string>
#include <unordered_map>
#include <iostream>
typedef void(*commandPtr)(const char* args);
std::unordered_map <std::string, commandPtr> commands;
void someCommand(const char* args)
{
std::cout << "some command with args : " << args << std::endl;
}
int main()
{
commands.insert(std::make_pair("someCommand", someCommand)); // add a command to the map
std::string command, args;
std::cin >> command >> args;
if (commands.find(command) == commands.end()) // the command doesn't exist
std::cout << "Command doesn't exist";
else
commands.find(command)->second(args.c_str()); // call the command with args
std::cin.get();
return 0;
}
This allows only one arbitrary argument to be used.
source to share