我正在寻找一种自动(作为编译/构建过程的一部分)在C中生成函数指针“表”的方法。
具体来说,我想生成一个结构数组,如:
typedef struct {
void (*p_func)(void);
char * funcName;
} funcRecord;
/* Automatically generate the lines below: */
extern void func1(void);
extern void func2(void);
/* ... */
funcRecord funcTable[] =
{
{ .p_func = &func1, .funcName = "func1" },
{ .p_func = &func2, .funcName = "func2" }
/* ... */
};
/* End automatically-generated code. */
...其中func1和func2在其他源文件中定义。
因此,给定一组源文件,每个源文件包含一个不带参数且返回void的函数,如何自动(作为构建过程的一部分)生成一个类似上面包含每个文件的数组文件的功能?我希望能够添加新文件,并在重新编译时自动将它们插入到表中。
我意识到这可能无法单独使用C语言或预处理器来实现,因此请考虑任何常见的* nix风格的工具公平游戏(例如make,perl,shell脚本(如果必须))。
你可能想知道为什么有人想要这样做。我正在为常见的数学例程库创建一个小的测试框架。在这个框架下,将会有许多小的“测试用例”,每个测试用例只有几行代码来运行每个数学函数。我希望每个测试用例都作为一个简短的函数存在于自己的源文件中。所有测试用例都将构建到单个可执行文件中,并且可以在调用可执行文件时在命令行上指定要运行的测试用例。 main()函数将搜索表,如果找到匹配项,则跳转到测试用例函数。
自动化构建测试用例“目录”的过程可确保测试用例不被遗漏(例如,因为有人忘记将其添加到表中)并使维护者很容易添加新的测试用例将来的测试用例(例如,只需在正确的目录中创建一个新的源文件)。
希望有人在那之前做过类似的事情。谢谢,StackOverflow社区!
答案 0 :(得分:6)
如何将宏列表设为
#define FUNC_LIST \
FUNC( func1 ) \
FUNC( func2 ) \
FUNC( func3 ) \
FUNC( func4 ) \
FUNC( func5 )
然后将extern
定义扩展为
#define FUNC( _name ) extern void _name(void);
FUNC_LIST
#undef FUNC
然后将表格展开为
#define FUNC( _name ) { .p_func = &_name, .funcName = #_name },
funcRecord funcTable[] = {
FUNC_LIST
};
#undef FUNC
如果您对测试函数有严格的命名约定,另一个建议是使用函数dlsym
并将句柄设置为RTLD_DEFAULT
并编写一个函数来尝试查看所有函数at启动。
实施例
#include <stdio.h>
#include <dlfcn.h>
void test2() {
printf("Second place is the first loser!\n");
}
void test42() {
printf("Read The Hitchhikers Guide To The Galaxy!\n");
}
int main() {
int i;
for (i=1; i<100; i++) {
char fname[32];
void (*func)();
sprintf(fname, "test%d", i);
func = dlsym(RTLD_DEFAULT, fname);
if (func)
func();
}
return 0;
}
答案 1 :(得分:3)
如果您使用的是MSVC,则可以使用
dumpbin /symbols foo.obj > foo_symbols.txt
将所有符号名称(不仅仅是函数)放入文本文件中。然后解析生成的文件以提取函数名称。函数将是External
UNDEF
个符号
或者,您可以将对象链接到临时exe或dll,然后查看链接器生成的.MAP文件以获取函数名称。
或者您可以编写自己的代码来解析.obj文件,它们采用修改后的COFF格式,找到符号表并对其进行解码并不困难。我对COFF格式进行了部分解码,一次到达符号表,编写代码花了几天时间。 http://en.wikipedia.org/wiki/COFF
答案 2 :(得分:3)
听起来像是代码生成的工作。选择您选择的脚本语言,并弄清楚如何从相应的标题中提取函数的名称并写出
{ .p_func = &functionname, .funcName = "functionname" },
然后告诉你构建系统来生成头文件头文件。在make中它可能看起来像
UTILITY_FUNCTION_HEADER:= func1.h func2.h func3.h
func_table.h: ${UTILITY_FUNCTION_HEADERS}
write_header.sh > $@
for file in @^; do extract_function_name.sh >> $@; done
write_footer.sh >>$@
答案 3 :(得分:1)
关于提取问题,我想我会标记我想要导出的函数,不知何故,然后在构建过程中提取它。
您可以使用“语义宏”来获取文本(即除了向外部工具提供语义信息之外什么都不做的宏):
#define TEST_CASE(f) f
T TEST_CASE(f)(D x, ...)
{
/* ... */
}
然后,您可以使用sed或awk或您喜欢的任何内容轻松提取该内容,并根据该列表以正确的格式创建列表。这是awk中的一些简单代码,因为这是我最熟悉的,但你可能想要使用别的东西:
match($0, /TEST_CASE\([a-zA-Z_][a-zA-Z_0-9]*\)/) {
name = substr($0, RSTART, RLENGTH)
sub(/^TEST_CASE\(/, "", name)
sub(/\)$/, "", name)
funcs[name]
}
END {
for (f in funcs)
printf "func_type %s;\n", f
print "funcRecord funcTable[] = {"
for (f in funcs)
printf "\t{ .p_func = %s, .funcName = \"%s\" },\n", f, f
print "};"
}
如果您要对名称进行排序(对bsearch()有用),我建议使用三个过滤器:一个提取过滤器(这里适合单行程),排序(1),然后是生成过滤器(我在这里使用awk)。但是,您必须单独生成页眉/页脚,并进行两次传递或将提取结果存储在临时文件中,以生成extern声明和数组条目。
我认为尝试使用给定原型提取函数不是一个好主意,例如虚空(无效)。更好地使用typedef(在我的示例中为func_type)和一个显式语义宏,恕我直言它更强大(对于更改,以及不同的编码样式,例如将返回类型单独放在一行而不是)。
剩下的就是将生成传递添加到你的makefile中,就像在dmckee的回复中一样(尽管你实际上想要将所有生成的代码放在.c而不是.h中,我认为)。为了完整起见,这是我的版本:
TEST_SRCS= test1.c test2.c test3.c
test_funcs.c: ${TEST_SRCS}
echo '#include "test.h"' >$@
awk -f extract_test_funcs.awk ${TEST_SRCS} >>$@
答案 4 :(得分:0)
C编译器就是这么做的。在Linux系统下,尝试在已编译的C文件上调用nm foo.o
。它将打印出包含所有功能的“符号表”,包括静态功能。
编辑:困难在于从源代码中提取信息(“有一个名为'func1()'的函数)。它需要解析C源文件。 C编译器已经这样做了(这是它的工作)所以使用C编译器的输出是有意义的。输出是一个目标文件,其中包含包含该信息的符号表。因此,我们的想法是解析nm
的输出并生成定义“函数表”的C源文件。这可以从makefile自动完成,以便始终生成表。
答案 5 :(得分:0)
我倾向于采用不同的方式。当我开发一些我经常想到的东西时:对于这样一个函数,在其中包含代码的好的C文件,比如/project-devel/project/new_function.c
。当我写这篇文章的时候,我也倾向于创建/project-devel/tests/test_new_function.c
和/project-devel/tests/make_new_function_test
,你猜测它会构建一个测试二进制文件,包括测试新函数所需的一切。
我想这个问题可以让你自动化吗?我不打算为它编写代码(因为我没有考虑过)但是你可以创建一个perl / python脚本来创建makefile,new function.c / .h和test bootstrap。您可以通过重新编写类似r"#include \\\"(\s+?).h\\\""
的内容来编写所需的包含,并创建一个test-funct“更新脚本”,根据新信息重新创建测试用例/ makefile。它仍然依赖于你编写实际的test.c但这对于设置这个想法是微不足道的。
只是一个想法。