在main()之前的编译时或运行时初始化函数指针的全局数组
我正在尝试在C或C ++中在编译时初始化函数指针的全局数组。 像这样的东西:
module.h中
typedef int16_t (*myfunc_t)(void); extern myfunc_array[];
module.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文件的符号链接,以便创建不同的目标文件:func1.o,func2.o,func3.o,…,funcN .o。使用g++ -DNUMBER=N
定义NUMBER
#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来生成它们。 function的排序并不重要,我可以解决这个问题。 有更简洁/更聪明的方法吗?
我打算建议这个问题更多的是关于C,但是在第二个想法,你想要的是一个函数指针的全局容器,并在其中注册可用的函数。 我相信这叫做Singleton (不寒而栗)。
你可以使myfunc_array
成为一个向量,或者包装一个C等价物,并提供一个函数来将myfunc
推入其中。 最后,你可以创建一个类(你可以在C中再做一次),它接受一个myfunc
并将其推送到全局数组中。 这将全部发生在主要被调用之前。 以下是一些让您思考的代码片段:
// a header extern vector myfunc_array; struct _register_myfunc { _register_myfunc(myfunc lolz0rs) { myfunc_array.push_back(lolz0rs); } } #define register_myfunc(lolz0rs) static _register_myfunc _unique_name(lolz0rs); // a source vector myfunc_array; // another source int16_t myfunc_1() { ... } register_myfunc(myfunc_1); // another source int16_t myfunc_2() { ... } register_myfunc(myfunc_2);
请记住以下内容:
- 您可以通过操纵链接步骤来控制function的注册顺序。
- 翻译单元范围变量的初始化发生在调用main之前,即注册将完成。
- 您可以使用一些宏魔法和
__COUNTER__
生成唯一的名称。 可能还有其他一些我不知道的偷偷摸摸的方式。 看到这些有用的问题:- C中未命名的参数
- 粘贴标记时出现意外的预定义宏行为
- 如何使用宏在C ++中生成随机变量名?
如何创建一个低级函数注册表
首先,创建一个宏,在特殊部分中放置指向函数的指针:
/* 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
function:
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[];
现在您可以访问您的arrays:
/* 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而不是numeric。
我用gcc 4.3.0测试了这个(虽然gcc部件已经可以使用了很长时间)和ld 2.18.50(你需要一个相当近的ld for INSERT AFTER
magic)。
这与编译器和链接器共同执行全局ctors的方式非常相似,因此使用静态C ++类构造函数来注册函数并且更加便携将会容易得多。
您可以在Linux内核中找到相关示例,例如__initcall
与此非常相似。
您的解决方案听起来太复杂,而且容易出错。
无论如何,你用脚本(或者可能是make)来检查你的项目,将-D
选项放到编译器中。 所以我想你保留了所有函数的列表(分别是定义它们的文件)。
我会为所有函数使用专有名称,不使用编号方案,然后我将使用该脚本生成文件“module.cpp”并使用名称初始化表。
为此,您只需在一个位置保留所有function(可能还有文件名)的列表。 我认为,这可能比你的实际方案更容易保持一致。
编辑:想到它,即使这也可能是过度工程。 如果你必须在某个地方维护一个你的函数列表,为什么不在文件“module.cpp”里面呢? 只需在其中包含所有函数的所有头文件,并将它们列在表的初始值设定项中。
既然你允许C ++,答案显然是肯定的,模板:
template int16_t myfunc() { /* N is a const int here */ } myfunc_array[] = { myfunc<0>, myfunc<1>, myfunc<2> }
现在,您可能想知道是否可以使用某个宏创建该可变长度初始化列表。 答案是肯定的,但需要的宏是丑陋的。 所以我不打算在这里写它们,而是指向Boost :: Preprocessor
但是,你真的需要这样的arrays吗? 你真的需要为myfunc<0>
命名myfunc_array[0]
吗? 即使你需要运行时参数( myfunc_array[i]
),还有其他技巧:
inline template int16_t myfunc_wrapper(int i) { assert (i : myfunc_wrapper(i-1); } inline int16_t myfunc_wrapper(int i) { return myfunc_wrapper(i); // NUMBER is defined on with g++ -DNUMBER=N }
好的,我根据Matt Joiner的提示制定了一个解决方案:
module.h中
typedef int16_t (*myfunc_t)(void); extern myfunc_array[]; class FunctionRegistrar { public: FunctionRegistrar(myfunc_t fn, int fn_number) { myfunc_array[fn_number - 1] = fn; // ensures correct ordering of functions (not that important though) } }
module.cpp
#include "module.h" myfunc_array[100]; // The size needs to be #defined by the compiler, probably
func1.cpp,func2.cpp,… funcN.cpp
#include "module.h" static int16_t myfunc(void) { ... } static FunctionRegistrar functionRegistrar(myfunc, NUMBER);
感谢大家!