I need some help with C++, please!
I'm writing a command parser for a small text-based game, and I've run into some problems. The parser is supposed to read and parse commands entered by the player.
The most obvious and straightforward solution to this could be something like this (written in pseudo-code):
command <- read input from the player
if command == COMMAND1
do command1
else if command == COMMAND 2
do command2
...
I'm writing in C++, so I was thinking I could solve this by using an associative map and function pointers. I'm not that familiar with using functions pointers, so that may be why I'm having problems. What I want to do, is to have some sort of loop that waits for input, parse the input that is inserted, and calls a function depending on the command given. Here's some C++-ish pseudo-code describing what I am thinking:
while(1) {
cin >> input;
char * tok = strtok(input, " ")
functionpointer fptr = command_map.find(tok);
... // here, I get stuck on what to do..
}
So I hope I make myself somewhat clear on what I want to happen. The player could have had input something like
> go south
and I could have finished the code with something like:
destination = strtok(NULL, " ");
fptr(destination);
Basically, the value returned from the map would be the function that performs the command "go", and that function apparently takes one argument, the destination. Again, this is some C++-pseudo-ish code. So I got the command "go" covered. But now say that I want to have the follwing command:
> attack troll with sword
Now I feel that I need to do something like:
while(1) {
cin >> input;
char * tok = strtok(input, " ")
functionpointer fptr = command_map.find(tok);
if(tok == "go"){
destination = strtok(NULL, " ");
fptr(destination);
} else if (tok == "attack") {
target = strtok(NULL, " ");
weapon = strtok(NULL, " ");
fptr(target, weapon);
}
}
Again, this is pseudo-code. You probably see what I get hung up on: I have this map of functions pointers, but because I have variable number of arguments and type of arguments because I want to call different functions depending on what I got as the input, so I could've just done this without a map and function pointers like I showed you first. Is there some way I can make this more general, without having to have some if-else clause to figure out how many arguments to pass?
I hope you understand what I need help with :) Thank you for reading!
Instead of having your main loop reading all the arguments needed for a 'passive' function, you can change your design to follow the Command design pattern, and have your function/command object do the argument parsing. That way you avoid needing to know the function's signature upfront.
You can use the Chain of Responsibility to find the proper Command, and let the Command consume the next tokens.
An example, using streams instead of strtok (heck we're C++ here, right?) - warning: uncompiled, untested, C++ish pseudo-code:
An implemented command:
And some main loop:
You can refine this idea with stronger utility functions etc...
If you expect a really hard grammar, it may be better to step over to a 'real' parser framework, like e.g. boost::spirit.
Have you thought about going a little OO. Have an abstract class say "Command" and have specialized classes for the specific commands.
Have a factory create the command objects based on the command entered by the user.
In the client get the command object and call the "Execute()" method
You can use auto pointer for better memory management.
A better solution would be to have all of your functions take the same arguments. A good idea would be to first completely tokenize your input (say, into a vector of strings), and then pass that array to the functions. You could then use an associative container (such as a hash table or a
std::map
) to map command tokens to handler functions.For example:
Only function parameter you need is the rest of the command line. Each function should tokenize it accordingly to its needs