简体   繁体   English

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

[英]C++ Function pointers with unknown number of arguments

I need some help with C++, please! 我需要一些C ++帮助!

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. 我正在用C ++编写,所以我想我可以通过使用关联映射和函数指针来解决这个问题。 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: 这里有一些C ++ - ish伪代码描述了我的想法:

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. 基本上,从映射返回的值将是执行命令“go”的函数,并且该函数显然需要一个参数,即目标。 Again, this is some C++-pseudo-ish code. 同样,这是一些C ++ - 伪代码。 So I got the command "go" covered. 所以我得到了命令“go”。 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? 有没有什么方法可以使这更通用,而不必有一些if-else子句来计算传递多少参数?

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. 您可以更改设计以遵循Command设计模式,并让您的函数/命令对象执行参数解析,而不是让主循环读取“被动”函数所需的所有参数。 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. 您可以使用责任链来查找正确的命令,并让Command使用下一个令牌。

An example, using streams instead of strtok (heck we're C++ here, right?) - warning: uncompiled, untested, C++ish pseudo-code: 一个例子,使用流而不是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;
};

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 eg boost::spirit . 如果你期望一个非常难的语法,那么最好转到一个“真正的”解析器框架,比如boost :: spirit

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. 然后,您可以使用关联容器(例如哈希表或std::map )将命令令牌映射到处理程序函数。

For example: 例如:

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
}

Have you thought about going a little OO. 你有没有考虑过一点点OO。 Have an abstract class say "Command" and have specialized classes for the specific commands. 有一个抽象类说“Command”并具有特定命令的专用类。

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

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

Have a factory create the command objects based on the command entered by the user. 让工厂根据用户输入的命令创建命令对象。

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

In the client get the command object and call the "Execute()" method 在客户端获取命令对象并调用“Execute()”方法

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

You can use auto pointer for better memory management. 您可以使用自动指针来更好地管理内存。

Only function parameter you need is the rest of the command line. 您需要的唯一功能参数是命令行的其余部分。 Each function should tokenize it accordingly to its needs 每个函数都应根据需要对其进行标记

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM