定义一个 C 函数数组

7

我有一堆C函数,它们被分配给一个函数指针数组,类似于这样:

typedef int (*func)(int);

int SomeLongName1(int a) {         
  // ...
}                      

// ...

int SomeLongName1000(int a) {         
  // ...
}                      

func f[] = { SomeLongName1, ... , SomeLongName1000 };

这是一项繁琐的工作,容易出现错误。例如,函数名称可能存在拼写错误,导致一个有效的函数仍被命名为错误的函数。或者,如果在末尾添加了新函数,则可能会忘记显式添加到函数指针列表中。
为了避免显式声明函数指针数组,我尝试过各种技巧,比如宏,但这样会使代码难以理解,需要了解宏的工作原理,而且我通常对它们不满意。
我想做的是像这样的事情:
typedef int (*func)(int);

func f[] = {             
  int SomeLongName1(int a) {         
    // ...
  }                      
  // ...                   
  int SomeLongName1000(int a) {         
    // ...
  }                      
};                       

这样,数组将自动创建,如果有某种方法可以在末尾放置一个空指针,以便我可以确定有多少函数指针,那就太好了。
然而,以上内容不是有效的C语言,我也找不到任何实现这个目标的方法。如果是编译器特定的东西(例如GCC扩展),那也没关系。
所有的函数在编译时都是静态已知的,所以我想避免对函数指针数组进行任何运行时初始化 - 即使我找到一种方法也是如此。
这个相关的问题 How to define an array of functions 看起来是在问同样的问题,但没有引申到逻辑上的结论。具体来说,我不希望为了节省时间和避免错误而重新输入我已经输入过的任何内容。
4个回答

4
如果您不关心数组中函数的顺序,并且愿意使用GCC扩展,那么您可以使用一系列初始化器(构造函数)函数来实现您想要的效果。显然,由于定义了大量额外的函数,这并不理想,但这确实是您可以考虑的一种方法。它在运行时构建数组。
定义函数append以将单个函数附加到数组中(如有必要,则重新分配)。然后,代码基本上是这样的。
#define ARRAYFUNC(name) int name(int); \
    void __attribute__((constructor)) __init_##name(void) { append(func); } \
    int name(int a)

ARRAYFUNC(func1) {
    ...
}

ARRAYFUNC(func2) {
    ...
}

很棒的想法,让链接器来确定哪些内容属于数组。好处是,即使这些函数不是在同一个文件中定义的,这种方法也能够奏效。你得到了我的支持 :-) - cmaster - reinstate monica

3

您可以使用C预处理器(X-Macros)来实现此目的:

#include <stdio.h>

// define a list of function names and bodies
#define FUNCS \
  FUNC(add, { return a+b; }) \
  FUNC(mul, { return a*b; }) \
  FUNC(div, { return a/b; })

// let the preprocessor make up the actual function implementations
#define FUNC(name, body) int name(int a, int b) body
FUNCS
#undef FUNC

typedef int (*func)(int, int);

// let the preprocessor populate the array of function pointers
func f[] = {
#define FUNC(name, body) name, 
FUNCS
#undef FUNC
};

// use it:
int main () {
    int a = 2, b = 3, i = 0;
    for (; i < sizeof(f)/sizeof(*f); i++) {
        printf("%d\n", f[i](a,b));
    }
    return 0;
}

输出结果为:
$ gcc test.c && ./a.out
5
6
0

2

如果我遇到这种情况(当然,只有在无法避免的情况下),我会使用预处理。不是来自C预处理器的那个,它没有以合理的语法提供所需功能,而是像 m4 这样真正强大的预处理器。

使用 m4,你的代码可以像这样:

define(`functionList', `, 0')
define(`functionArrayMember', `define(`functionList', `$1, 'FunctionList)$1')
define(`buildFunctionArray', `{ functionList }')

int functionArrayMember(SomeLongName1)(int a) {
    return a+1;
}
//...
int functionArrayMember(SomeLongName1000)(int a) {
    return a+1;
}

func f[] = buildFunctionArray();

你只需要为 functionArrayMember()buildFunctionArray() 提供正确的 m4 定义,就可以获得所需功能。

我真的很喜欢这个想法。在C程序的上下文中如何使用m4的某种教程会很不错(到目前为止在Google上没有找到...) - Michael
我已经添加了m4的定义,应该可以解决问题。虽然我没有测试过,但可能会有错误。这个想法是,functionList宏累积函数名称列表,以便稍后将它们转储到数组文字中。 - cmaster - reinstate monica
谢谢,这非常有帮助。请问使用m4运行C源代码的推荐方法是什么?我希望能够像现有预处理器那样进行操作,即不需要保留m4的中间输出文件进行编译。 - Michael
你可以尝试使用m4和编译器来创建一个管道,只需要告诉编译器从stdin而不是文件中读取即可。我通常喜欢使用一个中间文件,在我的Makefile中放置一个规则来运行.c.m4文件上的m4以生成一个.c文件,然后再进行正常的编译规则。这样,我就可以轻松地单独调试m4步骤。在这种简单的情况下,可能并不那么重要,但是我使用m4的东西基本上都需要这样做... - cmaster - reinstate monica

2

我认为没有其他方法可以做到你想做的事情。

你写的内容:

func f[] = { SomeLongName1, ... , SomeLongName1000 };

已经做到了最好。

也许您可以在函数名前加上前缀0000到1000,这样您就可以确保每个函数都在正确的位置上,放在函数指针数组中。

此外,如果您真的有1000个不同的函数,它们肯定有共同之处,可以将它们分类到几个数组中,从而减少编号的工作量,并且减少错误的可能性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接