I am working on an command-line application that receives specific user instructions. My solution was to create a CommandTable class that handles all the matching for the commands (in the form of regex-like strings) and their handler functions.
So far the class looks like this:
#include <string>
class CommandTable
{
public:
struct CommandStruct
{
std::string command;
int (*commandHandler)();
};
CommandTable(int tableSize);
int handleCommand(const std::string &command);
int setCommand(int index, const std::string &command, int (*handler)());
private:
CommandStruct *table;
int tableSize;
void commandNotFound();
};
And its implementation:
#include "CommandTable.h"
#include <iostream>
CommandTable::CommandTable(int tableSize) : tableSize(tableSize)
{
table = new CommandStruct[tableSize];
}
int CommandTable::handleCommand(const std::string &command)
{
for (int i = 0; i < tableSize; ++i)
{
if (std::regex_match(command, std::regex(table[i].command)))
{
table[i].commandHandler();
return 0;
}
}
commandNotFound();
return -1;
}
int CommandTable::setCommand(int index, const std::string &command, int (*handler)())
{
if (index > tableSize - 1 || index < 0)
{
std::cerr << "Invalid index for command table: " << index << std::endl;
exit(-1);
}
table[index].command = command;
table[index].commandHandler = handler;
return 0;
}
void CommandTable::commandNotFound()
{
std::cout << "Command not found" << std::endl;
}
The way I create each possible command is like:
#define COMMAND_TABLE_SIZE 3
int main()
{
CommandTable commandTable = CommandTable(COMMAND_TABLE_SIZE);
commandTable.setCommand(0, "initialize", initialize);
commandTable.setCommand(3, "initialize (.+)", initializeFromFile);
string inputCommand;
cout << "cli> ";
cin >> inputCommand;
do
{
commandTable.handleCommand(inputCommand);
cout << "cli> ";
cin >> inputCommand;
} while (inputCommand != "exit");
return 0;
}
THE PROBLEM:
Consider those two possible instructions:
- initialize
- initialize filename
And many other instructions can have or not a second form that have "arguments" or parameters, such as "save".
For the moment, the first should just prints "Initializing...", and the second should print the same along with the given filename. For that purpose, i created two different functions that do just that. The second function receives the filename as a parameter to print it.
int initialize()
{
cout << "Initializing..." << endl;
return 0;
}
int initializeFromFile(string filename)
{
cout << "Initializing from: " << filename << endl;
return 0;
}
The problem is that when defining the CommandStruct, it accepts a function that doesn't have parameters, so i can just call the setCommand function with the initialize handler, but adding the one with paramenters, initializeFromFile i get the following error:
argument of type "int (*)(std::string filename)" is incompatible with parameter of type "int (*)()"
And doing it the other way around, by defining the parameters in the definition of CommandStruct handler, now the first one gives the same error.
Even if i would be able to define those handlers without problems, now the matter is, how do i properly call them in the handleCommand function:
- How could i extract the argument from the input?
- How can i call the function depending on whether or not it has parameters?
- Is regex the proper way to define the commands?
- Is even a good way to work with possible commands like this?
I tried to define many ways to use the setCommand function so i can add handlers with or without parameters but that would produce some collissions and calling random handlers everytime.