tob*_*ier 9 c++ function-pointers
我需要一些C++帮助!
我正在为一个基于文本的小游戏编写一个命令解析器,我遇到了一些问题.解析器应该读取并解析播放器输入的命令.
对此最明显和直接的解决方案可能是这样的(用伪代码编写):
command <- read input from the player
if command == COMMAND1
do command1
else if command == COMMAND 2
do command2
...
Run Code Online (Sandbox Code Playgroud)
我正在用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..
}
Run Code Online (Sandbox Code Playgroud)
所以我希望我能清楚地说明我想要发生什么.玩家本可以输入类似的内容
> go south
Run Code Online (Sandbox Code Playgroud)
我可以用以下代码完成代码:
destination = strtok(NULL, " ");
fptr(destination);
Run Code Online (Sandbox Code Playgroud)
基本上,从映射返回的值将是执行命令"go"的函数,并且该函数显然需要一个参数,即目标.同样,这是一些C++ - 伪代码.所以我得到了命令"go".但是现在说我想要有以下命令:
> attack troll with sword
Run Code Online (Sandbox Code Playgroud)
现在我觉得我需要做一些事情:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
同样,这是伪代码.你可能会看到我被挂起的东西:我有这个函数指针的映射,但因为我有可变数量的参数和参数类型,因为我想根据我得到的输入调用不同的函数,所以我可以'我刚刚完成这个没有地图和函数指针,就像我先向你展示的那样.有没有什么方法可以使这更通用,而不必有一些if-else子句来计算传递多少参数?
我希望你明白我需要帮助:)谢谢你的阅读!
您可以更改设计以遵循Command设计模式,并让您的函数/命令对象执行参数解析,而不是让主循环读取"被动"函数所需的所有参数.这样您就可以避免需要事先知道函数的签名.
您可以使用责任链来查找正确的命令,并让Command使用下一个令牌.
一个例子,使用流而不是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;
};
Run Code Online (Sandbox Code Playgroud)
实施的命令:
struct Go : public Command {
Go():Command("Go"){}
virtual void parse_and_exec( istream& s ) {
string direction;
s >> direction;
... stuff with direction
}
};
Run Code Online (Sandbox Code Playgroud)
还有一些主循环:
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;
}
Run Code Online (Sandbox Code Playgroud)
您可以通过更强大的实用功能等来改进这个想法......
如果你期望一个非常难的语法,那么最好转到一个"真正的"解析器框架,比如boost :: spirit.
更好的解决方案是让所有函数都采用相同的参数。一个好主意是首先完全标记您的输入(例如,转换为字符串向量),然后将该数组传递给函数。然后,您可以使用关联容器(例如哈希表或 )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
}
Run Code Online (Sandbox Code Playgroud)