我正在使用一个简单的命令行应用程序,该应用程序接收ASCI文本并将其解释为命令。
我试图通过http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html上的示例来最小化此应用程序中的冗余。
例如: 考虑一个解释命名命令的C程序。可能需要一个命令表,可能是一个声明如下的结构数组:
struct command
{
char *name;
void (*function) (void);
};
struct command commands[] =
{
{ "quit", quit_command },
{ "help", help_command },
...
};
如果必须给每个命令名两次,一次在字符串常量中,一次在函数名中,则更清楚。将命令名称作为参数的宏可以使其不必要。字符串常量可以使用字符串化创建,函数名称可以通过将参数与`_command'连接来创建。以下是它的完成方式:
#define COMMAND(NAME) { #NAME, NAME ## _command }
struct command commands[] =
{
COMMAND (quit),
COMMAND (help),
...
};
现在,假设我想要一个命令字符串和索引(即:int)值,而不是字符串和函数指针。
struct command
{
char *name;
int command_idx;
};
现在,我有一种命名命令的方法,并且有一些我稍后可以用来以编程方式识别每个命令的索引。例如,我有一个对命令索引进行操作的switch语句。如果我想处理这些索引,我必须先手动设置值。
我可以手动创建枚举数据类型,但后来我在单独的枚举语句中定义了枚举常量。 IE: 枚举命令{cmd_quit = 0,cmd_help} 最后,我最终还是要输入两次命令名:一次是通过COMMAND()宏,一次是我的枚举。
是否有任何方法使用C预处理器允许我创建一个宏创建“命令”结构(使用字符串和int成员),并自动编号int值(command_idx),因为我通过添加更多命令COMMAND()宏?
我也知道我可以对每个可能的命令使用strcmp()调用,并与用户提供的输入进行比较,但我希望通过command_idx值直接索引命令,如反对每次strcmping对大量命令列表(即:O(1)而不是O(n))。我还想避免不惜一切代价多次输入命令名。
谢谢!
答案 0 :(得分:5)
您可以使用宏重新定义来实现此目的。首先,创建一个文件,只列出名为commands.inc
的命令:
COMMAND(quit)
COMMAND(help)
...
然后,在您的C源代码中,您可以#include "commands.inc"
多次,使用COMMAND()
的不同定义来控制其工作方式。例如:
struct command
{
char *name;
int command_idx;
};
#define COMMAND(NAME) CMD_ ## NAME,
enum command_enum {
#include "commands.inc"
};
#undef COMMAND
#define COMMAND(NAME) { #NAME, CMD_ ## NAME },
struct command commands[] =
{
#include "commands.inc"
};
#undef COMMAND
(请注意,此特定示例依赖于C99改进,允许在,
声明和复合初始化列表的列表末尾追加enum
- 您可以轻松解决C89中的问题最后添加一个虚拟条目。)
答案 1 :(得分:2)
问题:
是否有任何方法使用C预处理器允许我创建一个宏创建“命令”结构(使用字符串和int成员),并自动编号int值(command_idx),因为我通过添加更多命令COMMAND()宏?
是的,因为您已将问题标记为C ++:
#include <iostream>
#include <map>
#include <string>
using namespace std;
map< string, int > commands;
bool register_cmd( int id, string const& name )
{
commands[name] = id;
return true;
}
#define COMMAND( name ) \
int const name ## _cmd = __LINE__; \
bool const name ## _reg = register_cmd( name ## _cmd, #name )
COMMAND( exit );
COMMAND( help );
COMMAND( do_stuff );
int cmd_id( string const& name )
{
auto const it = commands.find( name );
return (it == commands.end()? -1 : it->second );
}
int main()
{
for( auto it = commands.begin(); it != commands.end(); ++it )
{
cout << it->first << " => " << it->second << endl;
}
cout << "Gimme a command, please: ";
string cmd; getline( cin, cmd );
switch( cmd_id( cmd ) )
{
case exit_cmd:
cout << "You typed an EXIT command, which has id " << exit_cmd << endl;
break;
default:
cout << "Hey, why not try an 'exit' command?" << endl;
}
}
我刚使用map
而不是花哨的新C ++ 11哈希表,因为map
适用于较旧的编译器,并且不需要在此处使用剃须纳秒。
干杯&amp;第h。,