c++ - 在 main() 之前在编译时或运行时初始化函数指针的全局数组

标签 c++ c

我试图在编译时用 C 或 C++ 初始化一个全局函数指针数组。像这样:

module.h

typedef int16_t (*myfunc_t)(void);
extern myfunc_array[];

模块.cpp

#include "module.h"
int16_t myfunc_1();
int16_t myfunc_2();
...
int16_t myfunc_N();

// the ordering of functions is not that important
myfunc_array[] = { myfunc_1, myfunc_2, ... , myfunc_N };

func1.cpp, func2.cpp, ... funcN.cpp(指向单个 func.cpp 文件的符号链接(symbolic link),以便创建不同的目标文件:func1.o、func2.o , func3.o, ... , funcN.o. NUMBER 是使用 g++ -DNUMBER=N)

定义的
#include "module.h"
#define CONCAT2(x, y) x ## y
#define CONCAT(x, y) CONCAT2(x, y)

int16_t CONCAT(myfunc_, NUMBER)() { ... }

使用g++-DNUMBER=N编译时,经过预处理后变为:

func1.cpp

...
int16_t myfunc_1() { ... }

func2.cpp

...
int16_t myfunc_2() { ... }

等等。

myfunc_N() 的声明和 myfunc_array[] 的初始化并不酷,因为 N 经常变化并且可能在 10 到 200 之间。我不想使用脚本或 Makefile 来生成它们。功能的顺序并不那么重要,我可以解决这个问题。有没有更简洁/更智能的方法来做到这一点?

最佳答案

如何制作低级函数注册表

首先,您创建一个宏以将指向您的函数的指针放在一个特殊的部分中:

/* original typedef from question: */
typedef int16_t (*myfunc)(void);

#define myfunc_register(N) \
    static myfunc registered_##myfunc_##N \
      __attribute__((__section__(".myfunc_registry"))) = myfunc_##N

静态变量名是任意的(永远不会被使用),但最好选择一个有表现力的名字。您可以通过将注册放在您的功能下方来使用它:

myfunc_register(NUMBER);

现在,当您(每次)编译您的文件时,它将在 .myfunc_registry 部分中有一个指向您的函数的指针。这将按原样编译,但如果没有链接描述文件,它对你没有任何好处。感谢 caf 指出相对较新的 INSERT AFTER 功能:

SECTIONS
{
    .rel.rodata.myfunc_registry : {
        PROVIDE(myfunc_registry_start = .);
        *(.myfunc_registry)
        PROVIDE(myfunc_registry_end = .);
    }
}
INSERT AFTER .text;

此方案最难的部分是创建整个链接器脚本:您需要将该片段嵌入到主机的实际链接器脚本中,这可能只能通过手动构建 binutils 来实现并检查编译树或通过 strings ld。很遗憾,因为我非常喜欢链接器脚本技巧。

使用 gcc -Wl,-Tlinkerscript.ld ... 链接 -T 选项将增强(而不是替换)现有的链接描述文件。

现在链接器将把你所有的指针和 section 属性聚集在一起,并在你的列表前后提供一个符号指向:

extern myfunc myfunc_registry_start[], myfunc_registry_end[];

现在你可以访问你的数组了:

/* this cannot be static because it is not know at compile time */
size_t myfunc_registry_size = (myfunc_registry_end - myfunc_registry_start);
int i;

for (i = 0; i < myfunc_registry_size); ++i)
    (*myfunc_registry_start[i])();

它们不会按任何特定顺序排列。您可以通过将它们放在 __section__(".myfunc_registry."#N) 中然后在链接器收集 *(.myfunc_registry.*) 中对它们进行编号,但排序会是 lexographic 而不是数字。

我已经用 gcc 4.3.0(尽管 gcc 部分已经可用很长时间了)和 ld 2.18.50(你需要一个相当新的 ld 来进行 INSERT AFTER 魔法测试).

这与编译器和链接器共同执行全局 ctors 的方式非常相似,因此使用静态 C++ 类构造函数来注册函数会容易得多,而且可移植性也大大提高。

你可以在 Linux 内核中找到这方面的例子,例如 __initcall 与此非常相似。

关于c++ - 在 main() 之前在编译时或运行时初始化函数指针的全局数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4152018/

相关文章:

c++ - 如何将随机(boost.random)库包装在一个类中?

c++ - 在 C/C++ 中直接写入内存地址的最短代码是什么?

c++ - MongoDB 2.4 C++ 驱动程序 - 对 `SSL_CTX_use_certificate_chain_file' 的 undefined reference

c - 指向指针和指针数组的指针

c - Windows中C语言获取进程用户名

c++ - 将派生类的 vector 重新解释为基类的 vector

c++ - 如何在 C++ 中声明一个全局变量

c - 有没有办法像 C 中的 PHP 那样将变量插入字符串中?

计算C程序中用户输入的个数

c - printf 的参数用作格式说明符的参数