我有read C不支持动态函数调用。我的程序有越来越多的测试用例作为单独的函数实现,如 -
int testcase1(void);
int testcase2(void);
int testcase3(void);
每次我添加一个新的测试用例时,我还必须将调用添加到我的主函数中,如 -
int main(int argc, char **argv){
assert(!testcase1());
assert(!testcase2());
assert(!testcase3());
}
我更愿意调用类似assert(!testcase*())
的内容,其中*匹配任何解析为程序中有效函数名的字符串。
你能想到一个更方便的解决方案吗?
答案 0 :(得分:7)
如果您的所有测试用例都具有相同的签名,则可以使用函数指针数组:
void (*func[])() = { testcase1, testcase2 };
for (size_t i = 0; i < sizeof(func)/sizeof(func[0]); i++) {
assert(!func[i]());
}
答案 1 :(得分:1)
当您添加新的测试用例时,最佳解决方案可能会编写一些额外的代码行 - 这确实不是一个大问题。我建议按照函数指针数组的方式进行操作,如另一个答案所示。
然而,只是为了表明如果你在问题上抛出丑陋的宏,C中的一切都是可能的,这里是一个不推荐的选择:
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#define TEST_CASES \ // list of "x macros"
X(testcase1) \
X(testcase2) \
X(testcase3)
#define X(func) bool func (void); // declare function prototypes
TEST_CASES
#undef X
bool (*const test_cases[])(void) = // array of read-only function pointers
{
#define X(func) &func, // point at each function
TEST_CASES
#undef X
};
int main (void)
{
for(size_t i=0; i<sizeof(test_cases)/sizeof(test_cases[0]); i++)
{
assert(test_cases[i]());
}
}
bool testcase1 (void) { puts(__func__); return true; }
bool testcase2 (void) { puts(__func__); return true; }
bool testcase3 (void) { puts(__func__); return false; }
输出:
testcase1
testcase2
testcase3
Assertion failed!
对于每个新的测试用例,您只需编写一个函数定义,然后将其添加到“x macro”列表TEST_CASES
。但是,你需要很好的理由在生产代码中引入这些丑陋的技巧!
答案 2 :(得分:0)
您可以使用function pointers。另请阅读closures(但C99或C11没有它们)和callbacks。
许多operating systems提供dynamic loading。在POSIX操作系统(例如Linux或MacOSX)上,您可以使用dlopen&amp ;;在某些库(或程序可执行文件)中从其名称获取函数指针(实际上是地址)。 dlsym。其他操作系统可能提供类似的功能。
最后,您应该考虑使用metaprogramming技术通过某些脚本(或某些发出C代码的程序)生成测试main
函数。因此,您可以编写一些内容,生成具有较长序列main
的测试assert
的C代码,并改进build procedure(例如Makefile
如果使用make适当地运行那个专门的C代码生成器。详细信息当然特定于您的代码。您可以添加一些约定(例如,添加一些特殊注释以供您的测试生成器解析等)。
答案 3 :(得分:0)
我决定关注@Nominal Animal和@Basile Starynkevitch的方法。在mymainprog.c中,我添加了 -
int runtests(void){
void *testh;
int (*testp)(void);
char *dlmsg;
int rc;
char funcname[8];
int testnum;
testh = dlopen("libsmtests.so", RTLD_LAZY);
if (!testh){
printf("%s\n", dlerror());
return 1;
}
dlerror();
for (testnum =1; testnum < 1000; testnum++){
sprintf(funcname,"testcase%d", testnum);
*(void **) (&testp) = dlsym(testh, funcname);
dlmsg = dlerror();
if (dlmsg == NULL) {
rc = (*testp)();
printf("%s called, rc=%d\n", funcname, rc);
}
}
dlclose(testh);
return 0;
}
我将我的测试用例添加到一个单独的文件(testcases.c)中,如下所示 -
int testcase1(void){
return [some testcase expression]
}
int testcase2(void){
return [another testcase expression]
}
然后将其编译为具有位置无关代码(-fPIC)到libsmtests.so的共享库。优点是输入略少,因为在向testcases.c添加新函数testNNNN()
的实现之后,我不需要编写对int testcaseNNN(void)
的调用的代码。