【问题标题】:Array of struct initialization using variadic macro使用可变参数宏的结构初始化数组
【发布时间】:2021-09-04 17:09:10
【问题描述】:

我有以下类型定义:

typedef struct {
  int (*function)(int argc, char *argv[]);
  char *name;
} command_t;

成员function是一个函数指针,成员name是一个字符串,它将存储函数的名称。

为了初始化command_t 类型的变量,我编写了以下宏:

#define COMMAND(x) (command_t){.function = x, .name = #x}

这是我目前初始化command_t数组的方式:

int ls(int argc, char *argv[]);
int echo(int argc, char *argv[]);
int cat(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);

command_t cmd_list[] = {COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir)};

我希望能够像这样初始化command_t 的数组:

command_t cmd_list[] = COMMAND(ls, echo, cat, mkdir);

command_t cmd_list[] = {COMMAND(ls, echo, cat, mkdir)};

我知道COMMAND 必须是可变参数宏才能这样做,但我不知道如何编写。

【问题讨论】:

  • 我不认为它可以用可变参数宏来完成。预处理器无法解构 VA_ARGS 并处理每个 arg。它只能对其体内的 VA_ARGS 进行简单的扩展。恕我直言,X 宏在这里更合适。
  • "我希望能够像这样初始化一个 command_t 数组" 为什么?它并没有提高可读性,它只是让你的代码看起来更神秘。如果有的话,你应该为整个初始化列表创建一个宏。

标签: arrays c struct initialization variadic-macros


【解决方案1】:

我认为您已经接近最易读的形式。尝试创建尽可能简单的代码。因此,与其发明一些奇怪的宏,C 程序员在不经过一堆宏的情况下都无法理解它的作用,不如为整个初始化列表创建一个宏——这是相当普遍的做法。

即:command_t cmd_lst[] = COMMAND_INIT; 然后:

#define COMMAND(cmd) { .function = cmd, .name = #cmd }
#define COMMAND_INIT { COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir) }

另一个选项是 X 宏。它们不一定更具可读性,但可能会减少其他地方的代码重复:

#define FUNC_LIST(X) \
  X(ls)              \
  X(echo)            \
  X(cat)             \
  X(mkdir)           \

...

#define COMMAND_INIT(func) { .function = func, .name = #func },  
command_t cmd_lst[] = { FUNC_LIST(COMMAND_INIT) };

【讨论】:

    【解决方案2】:
    //stringification of the argument Arg
    #define PP_STRINGIFY(Arg) PP_STRINGIFY_(Arg)
    #define PP_STRINGIFY_(Arg) #Arg
    
    //concatenation of the two arguments
    #define PP_CAT2(_1, _2) PP_CAT_(_1, _2)
    #define PP_CAT_(_1, _2) _1##_2
    
    //enumerate the number of arguments (min:1, max: 8)
    #define PP_VA_NUM_ARGS(...) PP_VA_NUM_ARGS_(__VA_ARGS__,8,7,6,5,4,3,2,1)
    #define PP_VA_NUM_ARGS_(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
    
    //a single command initializer
    #define COMMAND(x) (command_t){.function = x, .name = PP_STRINGIFY(x)}
    
    //command list initializer
    #define COMMAND_LST(...) { PP_CAT2(COMMAND_LST_,PP_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) }
    
    //implement as many as you need
    //maybe you need to extend PP_VA_NUM_ARGS (current max limit 8)
    #define COMMAND_LST_1(_1) COMMAND(_1)
    #define COMMAND_LST_2(_1,_2) COMMAND(_1), COMMAND(_2)
    #define COMMAND_LST_3(_1,_2,_3) COMMAND_LST_2(_1,_2), COMMAND(_3)
    #define COMMAND_LST_4(_1,_2,_3,_4) COMMAND_LST_3(_1,_2,_3), COMMAND(_4)
    
    typedef struct {
        int (*function)(int argc, char *argv[]);
        char *name;
    } command_t;
    
    int ls(int argc, char *argv[]);
    int echo(int argc, char *argv[]);
    int cat(int argc, char *argv[]);
    int mkdir(int argc, char *argv[]);
    
    int main()
    {
        command_t cmd_lst[] = COMMAND_LST(ls, echo, cat, mkdir);
        return 0;
    }
    

    CPP 输出方式

    gcc -E main.c
    

    给你

    command_t cmd_lst[] = { 
        (command_t){.function = ls, .name = "ls"}, 
        (command_t){.function = echo, .name = "echo"}, 
        (command_t){.function = cat, .name = "cat"}, 
        (command_t){.function = mkdir, .name = "mkdir"} 
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-22
      • 1970-01-01
      相关资源
      最近更新 更多