Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Function pointers with unknown number of arguments

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!

like image 934
tobier Avatar asked Nov 20 '10 19:11

tobier


People also ask

How many number of arguments functions can take?

Functions can define default argument values, functions can be called with keyword arguments, and functions can be written to accept any number of arguments.

What is the minimum number of arguments that functions can have?

Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero. The maximum number of arguments (and corresponding parameters) is 253 for a single function.

How do you declare a function pointer in C++?

We declare the function pointer, i.e., void (*ptr)(char*). The statement ptr=printname means that we are assigning the address of printname() function to ptr. Now, we can call the printname() function by using the statement ptr(s).


1 Answers

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:

struct ICommand {
   // if cmd matches this command,
   // read all needed tokens from stream and execute
   virtual bool exec( string cmd, istream& s ) = 0;
};

struct Command : public ICommand {
   string name;
   Command( string name ):name(name){}
   virtual bool exec( string cmd, istream& s ) {
      if( cmd != name ) return false;
      parse_and_exec( s );
      return true;
   }
   virtual void parse_and_exec( istream& s ) = 0;
};

An implemented command:

struct Go : public Command {
   Go():Command("Go"){}

   virtual void parse_and_exec( istream& s ) {
        string direction;
        s >> direction;
        ... stuff with direction
   }
 };

And some main loop:

 ICommand* commands [] = 
 { new Go()
 , new Attack()
 , new PickUp()
 ...
 , NULL // a sentinel
 };

 string cmd;
 cin >> cmd;
 while( cmd != "quit" ) {
    // find a command that can handle it
    // note: too simple to handle erroneous input, end of stream, ...
    for( ICommand* c = commands; c != NULL && !c->exec(cmd, cin); ++c );
    cin >> cmd;
 }

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.

like image 89
xtofl Avatar answered Oct 11 '22 23:10

xtofl