我正在尝试在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。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来生成它们。功能的排序并不重要,我可以解决这个问题。有更简洁/更聪明的方法吗?
答案 0 :(得分: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
静态变量名是任意的(它永远不会被使用)但是选择一个富有表现力的名字是很好的。您可以通过将注册放在函数下方来使用它:
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.*)
的链接器中对它们进行编号,但排序将是词法而不是数字。
我用gcc 4.3.0测试了这个(尽管gcc部分已经可以使用了很长时间)和ld 2.18.50(你需要一个相当近的INSERT AFTER
魔法的ld。
这与编译器和链接器共同执行全局ctors的方式非常相似,因此使用静态C ++类构造函数来注册函数并且更加便携将会更容易。
您可以在Linux内核中找到相关示例,例如__initcall
与此非常相似。
答案 1 :(得分:0)
我打算建议这个问题更多的是关于C,但是在第二个想法,你想要的是一个函数指针的全局容器,并在其中注册可用的函数。我相信这叫做 Singleton (不寒而栗)。
你可以使myfunc_array
成为一个向量,或者包含一个C等价物,并提供一个函数来将myfunc
推入其中。最后,您可以创建一个类(同样可以在C中执行此操作),它需要myfunc
并将其推送到全局数组中。这将全部发生在主要被调用之前。以下是一些让您思考的代码片段:
// 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);
请记住以下内容:
__COUNTER__
生成唯一名称。可能还有其他一些我不知道的偷偷摸摸的方式。看到这些有用的问题:
答案 2 :(得分:0)
您的解决方案听起来太复杂,而且容易出错。
无论如何,你用一个脚本(或者可能是make)来检查你的项目,以便将-D
选项放到编译器中。所以我想你保留了所有函数的列表(分别是定义它们的文件)。
我会为所有函数使用专有名称,不使用编号方案,然后使用该脚本生成文件“module.cpp”并使用名称初始化表。
为此,您只需在一个地方保留所有功能(可能还有文件名)的列表。我认为这可能比你的实际计划更容易保持一致。
编辑:想到这一点,即使这也可能是过度工程。如果你必须在某个地方维护一个你的函数列表,为什么不在文件“module.cpp”里面呢?只需包含所有函数的所有头文件,然后在表的初始值设定项中列出它们。
答案 3 :(得分:0)
由于你允许C ++,答案显然是肯定的,模板:
template<int N> int16_t myfunc() { /* N is a const int here */ }
myfunc_array[] = { myfunc<0>, myfunc<1>, myfunc<2> }
现在,您可能想知道是否可以使用某个宏创建该可变长度初始化列表。答案是肯定的,但需要的宏是丑陋的。所以我不打算在这里写,但请指向Boost::Preprocessor
但是,你真的需要这样的阵列吗?您真的需要myfunc_array[0]
的名称myfunc<0>
吗?即使您需要运行时参数(myfunc_array[i]
),也有其他技巧:
inline template <int Nmax> int16_t myfunc_wrapper(int i) {
assert (i<Nmax);
return (i==Nmax) ? myfunc<Nmax> : myfunc_wrapper(i-1);
}
inline int16_t myfunc_wrapper(int i) {
return myfunc_wrapper<NUMBER>(i); // NUMBER is defined on with g++ -DNUMBER=N
}
答案 4 :(得分:0)
好的,我根据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);
谢谢大家!