【问题标题】:Use typedef within struct for naming and indexing text commands在 struct 中使用 typedef 来命名和索引文本命令
【发布时间】:2011-11-01 00:19:04
【问题描述】:

我正在使用一个简单的命令行应用程序,它接收 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 值索引命令的直接方法,如反对 strcmp'ing 每次都针对大量命令列表(即: O(1) 而不是 O(n) )。我还想不惜一切代价避免多次键入命令名称。

谢谢!

【问题讨论】:

  • 为什么要索引?最好使用函数指针...
  • 一些编译器有一个__COUNTER__ 宏。你可以使用它。

标签: c++ c c-preprocessor


【解决方案1】:

您可以使用宏重新定义来实现这一点。首先,您创建一个简单列出您的命令的文件,名为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 中轻松解决该问题最后)。

【讨论】:

  • 完美。这正是我想要的。
【解决方案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 可以与旧的编译器一起使用,并且这里不需要节省纳秒。

干杯,

【讨论】:

  • 谢谢。我现在有 C99 和 C++ 两种方法来实现这一点。谢谢大家的帮助!
  • 您可以通过让register_cmd() 返回id,然后使用int const name ## _cmd = register_cmd(__LINE__, #name) 来消除bool 变量
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-14
  • 2020-11-13
  • 2019-09-02
  • 2016-05-29
  • 1970-01-01
  • 1970-01-01
  • 2015-03-21
相关资源
最近更新 更多