C ++具有未知数量参数的函数指针

时间:2010-11-20 19:01:15

标签: c++ function-pointers

我需要一些C ++方面的帮助!

我正在为一个基于文本的小游戏编写一个命令解析器,我遇到了一些问题。解析器应该读取并解析播放器输入的命令。

对此最明显和最直接的解决方案可能是这样的(用伪代码编写):

command <- read input from the player
if command == COMMAND1
    do command1
else if command == COMMAND 2
    do command2
...

我正在用C ++编写,所以我想我可以通过使用关联映射和函数指针来解决这个问题。我对使用函数指针并不熟悉,所以这可能就是我遇到问题的原因。我想要做的是,有一些等待输入的循环,解析插入的输入,并根据给定的命令调用函数。这里有一些C ++ - ish伪代码描述了我的想法:

while(1) {
 cin >> input;
 char * tok = strtok(input, " ")
 functionpointer fptr = command_map.find(tok);
 ... // here, I get stuck on what to do..
}

所以我希望自己能够清楚地知道自己想要发生什么。玩家可能输入类似

的内容
> go south

我可以用以下内容完成代码:

destination = strtok(NULL, " ");
fptr(destination);

基本上,从map返回的值将是执行命令“go”的函数,并且该函数显然需要一个参数,即目标。同样,这是一些C ++ - 伪代码。所以我得到了命令“go”。但现在说我想要掌握以下命令:

> attack troll with sword

现在我觉得我需要做类似的事情:

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);
   }
}

同样,这是伪代码。你可能会看到我被挂起的东西:我有这个函数指针的映射,但因为我有可变数量的参数和参数类型,因为我想根据我得到的输入调用不同的函数,所以我可以'我刚刚完成这个没有地图和函数指针,就像我先向你展示的那样。有没有什么方法可以让它更通用,而不必有一些if-else子句来计算要传递多少个参数?

我希望你明白我需要帮助:)谢谢你的阅读!

4 个答案:

答案 0 :(得分:6)

您可以更改设计以遵循命令设计模式,并让您的函数/命令对象执行参数解析,而不是让主循环读取“被动”函数所需的所有参数。这样你就可以避免需要事先知道函数的签名。

您可以使用责任链来查找正确的命令,并让命令使用下一个令牌。

一个例子,使用流而不是strtok(我们这里是C ++,对吗?) - 警告:未编译,未经测试,C ++ ish伪代码:

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;
};

已实施的命令:

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

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

还有一些主循环:

 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;
 }

您可以使用更强大的实用功能等来改进这个想法......

如果你期望一个非常难的语法,那么最好转到一个“真正的”解析器框架,例如boost::spirit

答案 1 :(得分:3)

更好的解决方案是让所有函数都使用相同的参数。一个好主意是首先完全标记您的输入(比如说,转换为字符串向量),然后将该数组传递给函数。然后,您可以使用关联容器(例如哈希表或std::map)将命令令牌映射到处理函数。

例如:

typedef std::vector<std::string> TokenArray;
typedef void (*CommandHandler)(const TokenArray&);
typedef std::map<std::string, CommandHandler> CommandMap;
void OnGo(const TokenArray& tokens)
{
    // handle "go" command
}
void OnAttack(const TokenArray& tokens)
{
    // handle "attack" command
}
// etc.

CommandMap handlers;
handlers["go"] = &OnGo;
handlers["attack"] = &OnAttack;
// etc.

while(1) {
  std::string input;
  cin >> input;
  std::istringstream tokenizer(input);
  TokenArray tokens;
  std::string token;
  while(tokenizer >> token)  // Tokenize input into whitespace-separated tokens
    tokens.push_back(token);
  CommandMap::iterator handler = handlers.find(tokens[0]);
  if(handler != handlers.end())
      (*handler)(tokens);  // call the handler
  else
      ; // handle unknown command
}

答案 2 :(得分:1)

你有没有考虑过一点OO。有一个抽象类说“命令”并具有特定命令的专用类。

struct Command
{
  virtual void Execute( const string &commandline ) = 0;
};

struct GoCommand: Command
{
  void Execute( const string &commandline )
  {
    // Do whatever you need here.
  }
}

让工厂根据用户输入的命令创建命令对象。

struct CommandFactory
{
  Command *GetCommand( const string &commandName )
  {
    if( commandNome == "go" )
    {
     return new GoCommand();
    }
    .........
    ........
  }
}

在客户端获取命令对象并调用“Execute()”方法

cin >> input;
char * commandName = strtok(input, " ")
Command *command = CommandFactory::Instance().GetCommand( commandName );
char * params = strtok(NULL, " ");
command->Execute( params );
delete command;

您可以使用自动指针来更好地管理内存。

答案 3 :(得分:1)

您需要的唯一功能参数是命令行的其余部分。每个函数都应根据需要对其进行标记