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

时间:2010-11-11 06:49:32

标签: c++ c

我正在尝试在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来生成它们。功能的排序并不重要,我可以解决这个问题。有更简洁/更聪明的方法吗?

5 个答案:

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

请记住以下内容:

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

谢谢大家!