在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 AFTERfunction:

 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); 

感谢大家!