我正在尝试在C或C++中在编译时初始化函数指针的全局数组.像这样的东西:
module.h中
typedef int16_t (*myfunc_t)(void);
extern myfunc_array[];
Run Code Online (Sandbox Code Playgroud)
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 };
Run Code Online (Sandbox Code Playgroud)
func1.cpp,func2.cpp,... funcN.cpp(指向单个func.cpp文件的符号链接,以便创建不同的目标文件: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)() { ... }
Run Code Online (Sandbox Code Playgroud)
使用g ++ -DNUMBER = N编译时,预处理后变为:
func1.cpp
...
int16_t myfunc_1() { ... }
Run Code Online (Sandbox Code Playgroud)
func2.cpp
...
int16_t myfunc_2() { ... }
Run Code Online (Sandbox Code Playgroud)
等等.
声明myfunc_N()和初始化myfunc_array[]并不酷,因为N经常变化,可能在10到200之间.我不想使用脚本或Makefile来生成它们.功能的排序并不重要,我可以解决这个问题.是否有更简洁/更聪明的方法来做到这一点?
Ben*_*son 18
首先,创建一个宏,在特殊部分中放置指向函数的指针:
/* original typedef from question: */
typedef int16_t (*myfunc)(void);
#define myfunc_register(N) \
static myfunc registered_##myfunc_##N \
__attribute__((__section__(".myfunc_registry"))) = myfunc_##N
Run Code Online (Sandbox Code Playgroud)
静态变量名是任意的(它永远不会被使用),但选择一个富有表现力的名称是很好的.您可以通过将注册放在函数下方来使用它:
myfunc_register(NUMBER);
Run Code Online (Sandbox Code Playgroud)
现在,当您编译文件时(每次),它将在该部分中指向您的函数.myfunc_registry.这将按原样编译,但如果没有链接器脚本,它将不会对您有任何好处.感谢caf,指出了相对较新的INSERT AFTER功能:
SECTIONS
{
.rel.rodata.myfunc_registry : {
PROVIDE(myfunc_registry_start = .);
*(.myfunc_registry)
PROVIDE(myfunc_registry_end = .);
}
}
INSERT AFTER .text;
Run Code Online (Sandbox Code Playgroud)
这个方案最难的部分是创建整个链接器脚本:您需要将该片段嵌入到主机的实际链接描述文件中,这可能只能通过手工构建binutils并检查编译树或通过来实现strings ld.这很遗憾,因为我非常喜欢链接器脚本技巧.
链接与gcc -Wl,-Tlinkerscript.ld ... 该-T选项将增强(而不是替换)现有的链接描述文件.
现在链接器将收集所有指向section属性的指针,并有助于在列表前后提供一个指向的符号:
extern myfunc myfunc_registry_start[], myfunc_registry_end[];
Run Code Online (Sandbox Code Playgroud)
现在您可以访问您的阵列:
/* 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])();
Run Code Online (Sandbox Code Playgroud)
它们不会有任何特定的顺序.您可以通过将它们放入__section__(".myfunc_registry." #N)并在链接器收集中对它们进行编号*(.myfunc_registry.*),但排序将是lexographic而不是numeric.
我用gcc 4.3.0测试了这个(虽然gcc部件已经可以使用了很长时间)和ld 2.18.50(你需要一个相当新的ld用于INSERT AFTER魔术).
这与编译器和链接器共同执行全局ctors的方式非常相似,因此使用静态C++类构造函数来注册函数并使其更具可移植性要容易得多.
您可以在Linux内核中找到相关示例,例如__initcall与此非常类似.
我本来打算建议这个问题更多地与 C 有关,但转念一想,您想要的是函数指针的全局容器,并将可用函数注册到其中。我相信这就是所谓的单例(不寒而栗)。
您可以创建myfunc_array一个向量,或者包装一个 C 等价物,并提供一个函数将myfuncs 推入其中。现在,您终于可以创建一个类(您也可以在 C 中执行此操作),该类接受 amyfunc并将其推入全局数组中。这一切都将在调用 main 之前立即发生。以下是一些可以让您思考的代码片段:
// a header
extern vector<myfunc> 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> myfunc_array;
// another source
int16_t myfunc_1() { ... }
register_myfunc(myfunc_1);
// another source
int16_t myfunc_2() { ... }
register_myfunc(myfunc_2);
Run Code Online (Sandbox Code Playgroud)
请记住以下几点:
__COUNTER__. 可能还有其他我不知道的偷偷摸摸的方式。请参阅这些有用的问题:
| 归档时间: |
|
| 查看次数: |
4318 次 |
| 最近记录: |