使用宏将项目附加到具有宏的数组中

时间:2010-09-03 07:50:29

标签: c arrays initialization c-preprocessor

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

例如:

DECLARE_CMD(f1, arg);
DECLARE_CMD(f2, arg);

从多个文件中调用DECLARE_CMD。

我希望将其预处理。

my_func_type my_funcs [] = {
   &f1,
   &f2
}

使用宏可以将项附加到静态数组吗?

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

4 个答案:

答案 0 :(得分:3)

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

实施例

假设您有三个文件a.c,b.c main.c和i.h

进入i.h

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,                           \
    }

加入a.c

#include "i.h"

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

ADD_FUNC(f1);

进入b.c

#include "i.h"

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

ADD_FUNC(f2);

进入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;
 }

重要

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

要解决此问题,我使用以下内容:

  1. 尝试打印链接描述文件:

    gcc -Wl,-verbose

  2. 在两者之间复制文字:

    <强> ============================================ ==

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

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

    OUTPUT_FORMAT(“elf64-x86-64”,“elf64-x86-64”,“elf64-x86-64”)

    ........

    1. 将您的部分添加到链接器脚本文件lnk.lds 之后的.data 部分之后(我的定义部分在示例中称为my_array):
    2.   

        __start_my_array = .;
        .my_array :
        {
          *(.my_array)      
        }
        __stop_my_array = .;
      
        

      1. 使用更新的链接描述文件编译程序,如下所示:

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

      2. 如果您输入字符串程序| grep“__start_my_array”你应该找到它。

答案 1 :(得分:2)

注意:在您的问题中,每行末尾都有分号。这将严重干扰任何使用这些宏的尝试。因此,它取决于DECLARE_CMD(...)行的位置和方式,以及是否可以修复分号问题。如果他们只是在一个专用的头文件中,你可以这样做:

#define DECLARE_CMD(func, arg) &func,

my_func_type my_funcs [] {
    #include "file_with_declare_cmd.h"
};

......变成了:

my_func_type my_funcs [] {
    &f1,
    &f2,
};

阅读The New C: X Macros以获得对此的详细解释。

如果您无法删除分号,则会将其处理为:

my_func_type my_funcs [] {
    &f1,;
    &f2,;
};

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

答案 2 :(得分:1)

是的,有可能。 通常的技巧是在一个(或多个)包含文件中包含所有DECLARE_CMD(func, args)行,并将这些行包含在具有宏的适当定义的不同位置。

例如:

在'commands.inc'文件中:

DECLARE_CMD(f1, args)
DECLARE_CMD(f2, args)

在某些源文件中:

/* function declarations */
#define DECLARE_CMD(func, args) my_func_type func;
#include "commands.inc"
#undef DECLARE_CMD

/* array with poiners */
#define DECLARE_CMD(func, args) &func,
my_func_type* my_funcs[] = {
    #include "commands.inc"
    NULL
};

答案 3 :(得分:0)

您实际上可以使用单个宏来设置函数指针,执行函数声明,设置枚举以访问函数指针和要在错误消息中使用的字符串,稍后您可以在switch()中使用它。

#define X_MACRO(OP) \
  OP(addi, int x, int y) \
  OP(divi, int x, int y) \
  OP(muli, int x, int y) \
  OP(subi, int x, int y)

#define AS_FUNC_PTR(x,...) x,
#define AS_FUNC(x,...) int x(__VA_ARGS__);
#define AS_STRINGS(x,...) #x,
#define AS_ENUMS(x,...) ENUM_##x,

X_MACRO(AS_FUNC)

typedef int (*foo_ptr_t)( int, int );
foo_ptr_t foo[] = { X_MACRO(AS_FUNC_PTR) };

char *foo_strings[] = { X_MACRO(AS_STRINGS) };

enum foo_enums { X_MACRO(AS_ENUMS) };

/** example switch()
#define AS_CASE(x,...) ENUM_x : x(i,j);break;
switch (my_foo_enum){
  X_MACRO(AS_CASE)
  default: do_error();
}
**/