使用预处理器的顺序函数名称
Sequential function names using preprocessor
我正在用 C 编写一个简单的单元测试库来 运行 对代码进行一些测试,我很好奇是否可以使用 macros/the 预处理器使过程更高效。是的,我意识到我可能不应该自己写,但我仍然想知道这是否可能。
这是我目前的做法:
#include <stdio.h>
/* Each test exits with status 1 if fail */
void test_00() { ... }
void test_01() { ... }
...
void test_09() { ... }
#define NUM_TESTS 10
void (*TESTS[NUM_TESTS])() = {test_00, test_01, ..., test_09};
/* Only want to run the test number passed in on the command line */
int main(int argc, char *argv) {
int test_num = atoi(argv[1]); // Error checking in actual code
TESTS[test_num]();
}
特别是,我有兴趣摆脱 TESTS
数组,我必须在其中手动添加每个新测试。理想情况下,使用 NUM_TESTS
自动生成它,因为老实说它总是重复的。
不必特别笼统,我不会有超过100个测试用例所以测试编号2位数字就可以了,所有测试的命名方式完全相同。
抱歉,如果这个问题没有遵循某些准则。我没有合理的尝试去展示,甚至不知道究竟要寻找什么。
// Provide the counts as macros:
#define TEST_LISTS_0() test_00
//$ for i in $(seq 1 10); do echo "#define TEST_LISTS_$i() TEST_LISTS_$((i-1))(),$(printf "%02d" "$i")"; done
#define TEST_LISTS_1() TEST_LISTS_0(),test_01
#define TEST_LISTS_2() TEST_LISTS_1(),test_02
#define TEST_LISTS_3() TEST_LISTS_2(),test_03
#define TEST_LISTS_4() TEST_LISTS_3(),test_04
#define TEST_LISTS_5() TEST_LISTS_4(),test_05
#define TEST_LISTS_6() TEST_LISTS_5(),test_06
#define TEST_LISTS_7() TEST_LISTS_6(),test_07
#define TEST_LISTS_8() TEST_LISTS_7(),test_08
#define TEST_LISTS_9() TEST_LISTS_8(),test_09
#define TEST_LISTS_10() TEST_LISTS_9(),test_10
// etc.
// Then just reexpand the macro expansion with the prefix:
#define TEST_LIST_GEN_IN(x) TEST_LISTS_##x()
#define TEST_LIST_GEN(x) TEST_LIST_GEN_IN(x)
#define NUM_TESTS 10
void (*TESTS[NUM_TESTS])() = {TEST_LIST_GEN(NUM_TESTS)};
have it be automatically generated using NUM_TESTS since it's honestly always repetitive.
使用 gcc 编译器(或仅支持该编译器的编译器)时,您可以使用 section
并在其中放置指向测试的指针。
#define XCONCAT(a, b) CONCAT(a, b)
#define CONCAT(a, b) a##b
// adds a pointer to the test function inside section tests
#define ADD_TEST(TEST_FUNCTION) \
__attribute__((__section__("tests"))) \
void (*XCONCAT(_test_, __LINE__)) = TEST_FUNCTION
void test_01() {
printf("%s\n", __func__);
}
// registers the test
ADD_TEST(test_01);
void test_02() {
printf("%s\n", __func__);
}
ADD_TEST(test_02);
int main() {
extern void (*__start_tests)();
extern void (*__stop_tests)();
int count = &__stop_tests - &__start_tests;
printf("%d\n", count);
for (int i = 0; i < count; ++i) {
(&__start_tests)[i]();
}
}
// will print 2 test_01 test_02
您可以使用 dlsym()
.
在运行时动态加载和调用符号
// compile with `gcc -Wall -o test -Wl,--export-dynamic test.c -ldl`
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
/* Each test exits with status 1 if fail */
void test_00() { exit(10); }
void test_01() { exit(11); }
void test_09() { exit(19); }
/* Only want to run the test number passed in on the command line */
int main(int argc, char **argv)
{
void (*test)(void);
char symname[32];
snprintf(symname, sizeof(symname), "test_%s", argv[1]);
if ((test = dlsym(NULL, symname)) == NULL)
{
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
test();
}
我正在用 C 编写一个简单的单元测试库来 运行 对代码进行一些测试,我很好奇是否可以使用 macros/the 预处理器使过程更高效。是的,我意识到我可能不应该自己写,但我仍然想知道这是否可能。
这是我目前的做法:
#include <stdio.h>
/* Each test exits with status 1 if fail */
void test_00() { ... }
void test_01() { ... }
...
void test_09() { ... }
#define NUM_TESTS 10
void (*TESTS[NUM_TESTS])() = {test_00, test_01, ..., test_09};
/* Only want to run the test number passed in on the command line */
int main(int argc, char *argv) {
int test_num = atoi(argv[1]); // Error checking in actual code
TESTS[test_num]();
}
特别是,我有兴趣摆脱 TESTS
数组,我必须在其中手动添加每个新测试。理想情况下,使用 NUM_TESTS
自动生成它,因为老实说它总是重复的。
不必特别笼统,我不会有超过100个测试用例所以测试编号2位数字就可以了,所有测试的命名方式完全相同。
抱歉,如果这个问题没有遵循某些准则。我没有合理的尝试去展示,甚至不知道究竟要寻找什么。
// Provide the counts as macros:
#define TEST_LISTS_0() test_00
//$ for i in $(seq 1 10); do echo "#define TEST_LISTS_$i() TEST_LISTS_$((i-1))(),$(printf "%02d" "$i")"; done
#define TEST_LISTS_1() TEST_LISTS_0(),test_01
#define TEST_LISTS_2() TEST_LISTS_1(),test_02
#define TEST_LISTS_3() TEST_LISTS_2(),test_03
#define TEST_LISTS_4() TEST_LISTS_3(),test_04
#define TEST_LISTS_5() TEST_LISTS_4(),test_05
#define TEST_LISTS_6() TEST_LISTS_5(),test_06
#define TEST_LISTS_7() TEST_LISTS_6(),test_07
#define TEST_LISTS_8() TEST_LISTS_7(),test_08
#define TEST_LISTS_9() TEST_LISTS_8(),test_09
#define TEST_LISTS_10() TEST_LISTS_9(),test_10
// etc.
// Then just reexpand the macro expansion with the prefix:
#define TEST_LIST_GEN_IN(x) TEST_LISTS_##x()
#define TEST_LIST_GEN(x) TEST_LIST_GEN_IN(x)
#define NUM_TESTS 10
void (*TESTS[NUM_TESTS])() = {TEST_LIST_GEN(NUM_TESTS)};
have it be automatically generated using NUM_TESTS since it's honestly always repetitive.
使用 gcc 编译器(或仅支持该编译器的编译器)时,您可以使用 section
并在其中放置指向测试的指针。
#define XCONCAT(a, b) CONCAT(a, b)
#define CONCAT(a, b) a##b
// adds a pointer to the test function inside section tests
#define ADD_TEST(TEST_FUNCTION) \
__attribute__((__section__("tests"))) \
void (*XCONCAT(_test_, __LINE__)) = TEST_FUNCTION
void test_01() {
printf("%s\n", __func__);
}
// registers the test
ADD_TEST(test_01);
void test_02() {
printf("%s\n", __func__);
}
ADD_TEST(test_02);
int main() {
extern void (*__start_tests)();
extern void (*__stop_tests)();
int count = &__stop_tests - &__start_tests;
printf("%d\n", count);
for (int i = 0; i < count; ++i) {
(&__start_tests)[i]();
}
}
// will print 2 test_01 test_02
您可以使用 dlsym()
.
// compile with `gcc -Wall -o test -Wl,--export-dynamic test.c -ldl`
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
/* Each test exits with status 1 if fail */
void test_00() { exit(10); }
void test_01() { exit(11); }
void test_09() { exit(19); }
/* Only want to run the test number passed in on the command line */
int main(int argc, char **argv)
{
void (*test)(void);
char symname[32];
snprintf(symname, sizeof(symname), "test_%s", argv[1]);
if ((test = dlsym(NULL, symname)) == NULL)
{
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
test();
}