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

如何在C语言中实现接收任意函数指针与可变参数的通用caller函数

C语言实现通用函数转发的解决方案

纯C语言没有C那种编译期类型推导、可变参数完美转发的语法能力,无法实现和C模板完全等价的类型安全、无额外依赖的通用caller_imp函数,可根据场景选择以下两种近似方案:

方案1:宏直接展开(推荐,无依赖、类型安全、零开销)

如果不需要在函数调用前后添加统一的封装逻辑(比如日志打印、耗时统计、错误处理等),完全可以去掉caller_imp层,直接用宏完成调用,原有代码几乎不用修改:

// 直接用宏完成转发,无需额外实现函数
#define caller(function, ...) function(__VA_ARGS__)

// 原有测试代码完全兼容
int test (int a)
{
    return a;
}

int test2(int a, int b)
{
    return a+b;
}

int test3(int a,char *str)
{
    return printf("%d: %s\n", a, str);
}

int main(int argc, char **argv)
{
    caller(test,33);
    caller(test2,44,55);
    caller(test3, 10, "hello world");
    return 0;
}
  • 优点:完全符合示例的调用方式,类型由编译器自动检查,没有任何运行时开销,兼容所有C标准
  • 缺点:如果要加统一的调用前后逻辑,只能写在宏里,会在每个调用点展开多份代码,无法封装到独立函数中。

方案2:依赖libffi实现运行时动态调用

如果你必须把通用逻辑封装到独立的caller_imp函数中,可使用跨平台的libffi库,它提供了运行时构造任意函数调用的能力,你需要手动传入函数的返回值类型、参数个数、每个参数的类型信息(C没有运行时类型信息,必须手动提供):

#include <stdio.h>
#include <ffi.h>
#include <stdarg.h>
#include <stdlib.h>

#define MAX_ARG_CNT 16

void caller_imp(ffi_type *ret_type, void (*func)(), int arg_cnt, ...)
{
    ffi_cif cif;
    ffi_type *arg_types[MAX_ARG_CNT];
    void *arg_values[MAX_ARG_CNT];
    va_list ap;

    va_start(ap, arg_cnt);
    for (int i = 0; i < arg_cnt; i++) {
        arg_types[i] = va_arg(ap, ffi_type*);
        arg_values[i] = malloc(arg_types[i]->size);
        // 按需补充更多类型的处理逻辑
        if (arg_types[i] == &ffi_type_sint) {
            *(int*)arg_values[i] = va_arg(ap, int);
        } else if (arg_types[i] == &ffi_type_pointer) {
            *(void**)arg_values[i] = va_arg(ap, void*);
        }
    }
    va_end(ap);

    // 构造调用上下文后执行函数
    if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, arg_cnt, ret_type, arg_types) == FFI_OK) {
        int ret; // 非int返回值可改为通用内存存储
        ffi_call(&cif, func, &ret, arg_values);
        // 此处可添加统一的后置处理逻辑
        printf("函数调用完成,返回值:%d\n", ret);
    }

    for (int i = 0; i < arg_cnt; i++) {
        free(arg_values[i]);
    }
}

// 宏定义简化调用
#define caller(func, ret_type, arg_cnt, ...) caller_imp(ret_type, func, arg_cnt, __VA_ARGS__)

// 测试函数定义同上文
int test (int a) {return a;}
int test2(int a, int b) {return a+b;}

int main(int argc, char **argv)
{
    // 调用时需手动指定返回值、参数个数、每个参数的类型和值
    caller(test, &ffi_type_sint, 1, &ffi_type_sint, 33);
    caller(test2, &ffi_type_sint, 2, &ffi_type_sint, 44, &ffi_type_sint, 55);
    return 0;
}
  • 优点:可以把通用逻辑完全封装到caller_imp函数中,支持任意签名的函数调用
  • 缺点:需要依赖第三方libffi库,调用时需要手动传入类型描述,写起来繁琐,且有一定运行时开销。

补充说明

如果既不能用第三方库,又必须把逻辑封装到函数中,标准C没有可移植的实现方案,只能针对特定平台的调用约定手写汇编实现,这种方式兼容性极差,不推荐。

内容的提问来源于stack exchange,提问作者Honneamise

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 00:06:03