在C中使用宏将项目追加到数组

coj*_*car 6 c arrays initialization c-preprocessor

我有一个数组(C语言),应在编译时初始化。

例如:

DECLARE_CMD(f1, arg);
DECLARE_CMD(f2, arg);
Run Code Online (Sandbox Code Playgroud)

从多个文件中调用DECLARE_CMD。

我希望对此进行预处理。

my_func_type my_funcs [] = {
   &f1,
   &f2
}
Run Code Online (Sandbox Code Playgroud)

是否可以通过宏将项目附加到静态数组?

我在gcc4上使用C99(具有GNU扩展名)。

elh*_*ɥןǝ 6

是的,您可以在编译时(而不是在运行时)构建动态数组(感谢 Mitchel Humpherys),其想法是在同一部分声明您的回调,如下所示:

例子:

假设你有三个文件 ac, bc main.c 和 ih

进入 ih

typedef void (*my_func_cb)(void);

typedef struct func_ptr_s {
       my_func_cb cb; /* function callback */
} func_ptr_t;

#define ADD_FUNC(func_cb)                        \
    static func_ptr_t ptr_##func_cb              \
    __attribute((used, section("my_array"))) = { \
        .cb = func_cb,                           \
    }
Run Code Online (Sandbox Code Playgroud)

交流

#include "i.h"

static void f1(void) {
   ....
}

ADD_FUNC(f1);
Run Code Online (Sandbox Code Playgroud)

进入公元前

#include "i.h"

static void f2(void) {
   ....
}

ADD_FUNC(f2);
Run Code Online (Sandbox Code Playgroud)

进入main.c

 #include "i.h"
 
 static void f3(void) {
   ....
 }

 ADD_FUNC(f3);   

 #define section_foreach_entry(section_name, type_t, elem)    \
     for (type_t *elem =                                      \
            ({                                                \
                extern type_t __start_##section_name;         \
                &__start_##section_name;                      \
            });                                               \
            elem !=                                           \
            ({                                                \
                extern type_t __stop_##section_name;          \
                &__stop_##section_name;                       \
            });                                               \
            ++elem)
            

 int main(int argc, char *argv[])
 {
    section_foreach_entry(my_array, func_ptr_t, entry) {
            entry->cb(); /* this will call f1, f2 and f3 */
    }

    return 0;
 }
Run Code Online (Sandbox Code Playgroud)

重要的

有时编译器会优化开始/结束部分变量,将它们清除,因此当您尝试使用它们时,您将遇到链接器错误:错误 LNK2019:未解析的外部符号...

为了解决这个问题,我使用以下方法:

  1. 尝试打印您的链接器脚本:

    gcc -Wl,-详细

复制两者之间的文本:

==================================================

在文件(例如lnk.lds)中,您应该看到如下内容:

/* -z combreloc 的脚本:组合和排序 reloc 部分 */

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64","elf64-x86-64")

…………

  1. 像这样您的部分添加到链接描述文件 lnk.lds部分 .data 之后(我定义的部分在示例中称为 my_array ):
  __start_my_array = .;
  .my_array :
  {
    *(.my_array)      
  }
  __stop_my_array = .;
Run Code Online (Sandbox Code Playgroud)
  1. 使用更新后的链接器脚本编译您的程序,如下所示:

    gcc -O3 -Xlinker -T"lnk.lds" file.c -o program

  2. 如果您键入字符串程序 | grep "__start_my_array"你应该能找到它。


det*_*tly 2

注意:在您的问题中,每行末尾都有分号。这将严重干扰任何使用这些宏的尝试。因此,这取决于在哪里以及如何DECLARE_CMD(...)找到这些行,以及是否可以解决分号问题。如果它们只是单独位于专用头文件中,您可以执行以下操作:

#define DECLARE_CMD(func, arg) &func,

my_func_type my_funcs [] {
    #include "file_with_declare_cmd.h"
};
Run Code Online (Sandbox Code Playgroud)

...变成:

my_func_type my_funcs [] {
    &f1,
    &f2,
};
Run Code Online (Sandbox Code Playgroud)

请阅读新的 C: X 宏以获得对此的详细解释。

如果您无法去掉分号,这将被处理为:

my_func_type my_funcs [] {
    &f1,;
    &f2,;
};
Run Code Online (Sandbox Code Playgroud)

...这显然是一个语法错误,所以这不起作用。