C语言中bench函数适配不同返回类型函数指针的更优替代方案咨询
问题描述
我有一个用于性能测试的bench函数,声明如下:
static inline unsigned long bench(void* (*funcPtr)(va_list), ...)
该函数通过接收返回void*的函数指针实现泛型,但遇到两个问题:
- 传入返回其他类型的函数时,编译器报错:
passing argument 1 of 'bench' from incompatible pointer type - 手动将函数指针强制转换为
void*时,触发ISO C标准错误:ISO C forbids passing argument 1 of 'bench' between function pointer and 'void *'
目前已知的解决办法是编写返回void*的包装函数,但要么为每个待测试函数单独写包装(效率极低),要么为每种数据类型写包装(仍有冗余开销),请问有没有更优的替代方案?
最优替代方案
1. 使用C11 _Generic宏实现编译时类型适配
_Generic是C11引入的类型泛型选择机制,能在编译时根据函数的返回类型自动生成适配的包装逻辑,无需手动编写大量重复的包装函数,且没有运行时开销。
示例实现
#include <stdarg.h> #include <stdint.h> // 原bench函数保持不变 static inline unsigned long bench(void* (*funcPtr)(va_list), ...) { va_list args; va_start(args, funcPtr); funcPtr(args); va_end(args); return 0; // 示例返回值,实际替换为真实计时结果 } // 针对不同返回类型的包装函数模板 #define WRAP_FUNC(ret_type) \ static void* wrap_##ret_type(va_list args) { \ ret_type (*func)(va_list) = va_arg(args, ret_type (*)(va_list)); \ va_list args_copy; \ va_copy(args_copy, args); \ func(args_copy); \ va_end(args_copy); \ return NULL; /* 若需要返回值可修改为返回对应指针,这里仅适配bench的void*要求 */ \ } // 预定义常见类型的包装函数 WRAP_FUNC(int) WRAP_FUNC(uint64_t) WRAP_FUNC(void) // 泛型选择宏,根据函数指针类型匹配对应的包装函数 #define BENCH(func, ...) \ bench(_Generic((func), \ void*(*)(va_list): (void*(*)(va_list))func, \ int(*)(va_list): (void*(*)(va_list))wrap_int, \ uint64_t(*)(va_list): (void*(*)(va_list))wrap_uint64_t, \ void(*)(va_list): (void*(*)(va_list))wrap_void \ ), func, __VA_ARGS__) // 测试用例 static int test_int_func(va_list args) { int a = va_arg(args, int); return a + 1; } static void test_void_func(va_list args) { // 无返回值操作 } int main() { BENCH(test_int_func, 10); BENCH(test_void_func); return 0; }
优点
- 编译时完成类型匹配,无运行时额外开销
- 仅需为每种返回类型定义一次包装模板,无需为每个测试函数单独编写包装
- 符合ISO C11标准,兼容性好
2. 修改bench函数为通用函数指针参数(非标准但实用)
如果可以接受编译器扩展(GCC、Clang等主流编译器均支持),可以将bench的第一个参数改为通用函数指针void (*)(void),然后在内部配合类型强制转换,绕过类型检查。这种方式无需包装函数,但要注意调用时的参数正确性。
示例实现
#include <stdarg.h> #include <stdint.h> // 修改bench的参数为通用函数指针 static inline unsigned long bench(void (*funcPtr)(void), ...) { va_list args; va_start(args, funcPtr); // 根据实际调用场景,将funcPtr转换为对应类型并调用 // 这里需要调用者保证参数匹配,否则会有未定义行为 ((void*(*)(va_list))funcPtr)(args); va_end(args); return 0; } // 测试用例 static int test_func(va_list args) { int a = va_arg(args, int); return a * 2; } int main() { // 直接传入函数指针,编译器可能会警告,但可通过强制转换消除 bench((void(*)(void))test_func, 5); return 0; }
注意事项
- 这种方式违反严格的ISO C标准,但主流编译器支持
- 调用时必须保证函数指针的类型和实际调用逻辑匹配,否则会导致未定义行为
- 适合对性能要求极高,且能严格控制调用场景的情况
3. 结合可变参数宏与函数指针强制转换(简化版)
如果不想使用_Generic,也可以用可变参数宏直接生成强制转换逻辑,减少手动转换的繁琐:
#define BENCH(func, ...) bench((void*(*)(va_list))(uintptr_t)func, __VA_ARGS__)
说明
- 通过将函数指针转换为
uintptr_t(整数类型,可安全存储函数指针),再转换为目标函数指针类型,绕过ISO C对函数指针和void*转换的限制 - 同样需要保证函数的参数和返回类型与bench内部的调用逻辑匹配,否则会有未定义行为
- 代码简洁,但兼容性依赖于
uintptr_t的支持(C99及以上)
内容的提问来源于stack exchange,提问作者Programmer_2147483647
相关产品推荐
相关产品推荐

