You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 13:07:46