如何将C语言中value_info数组转为函数实参并调用指定函数?
解决方案:C语言中基于函数表的动态参数调用
由于C是静态编译型语言,无法直接实现完全通用的任意参数个数/类型的动态调用,只能通过预定义参数个数分支+运行时类型判断的方式实现最多10个参数的调用支持。以下是具体实现方案:
核心思路
- 先通过函数名字符串从函数表中找到对应的
function_info结构体。 - 根据
function_info记录的参数个数(argc)和参数类型(argv数组),从value_info参数数组中提取并转换参数。 - 针对不同参数个数编写分支逻辑,转换函数指针为正确类型后调用,并处理返回值。
完整实现代码
#include <stdio.h> #include <string.h> #include <math.h> #include <stdbool.h> // 类型定义(复用你提供的代码) typedef int int_type; typedef float float_type; typedef double double_type; typedef bool bool_type; typedef char *string_type; typedef enum { INT_TYPE, FLOAT_TYPE, DOUBLE_TYPE, BOOL_TYPE, STRING_TYPE, } data_type; typedef struct function_info function_info; typedef struct { data_type type; union { int_type val_int; float_type val_float; double_type val_double; bool_type val_bool; string_type val_string; function_info val_function; } value; } value_info; #define MAX_ARGS 10 struct function_info { union { int (*f_int)(); float (*f_float)(); double (*f_double)(); bool (*f_bool)(); char *(*f_string)(); } function; int argc; data_type argv[MAX_ARGS]; data_type ret_type; }; struct init { char *fname; function_info fnct; }; struct init arith_fncts[] = { {"sin", {.function.f_double = sin, 1, {DOUBLE_TYPE}, DOUBLE_TYPE}}, {"cos", {.function.f_double = cos, 1, {DOUBLE_TYPE}, DOUBLE_TYPE}}, {"tan", {.function.f_double = tan, 1, {DOUBLE_TYPE}, DOUBLE_TYPE}}}; // 辅助函数:将value_info转换为double类型参数 double get_double_arg(value_info *arg) { switch(arg->type) { case INT_TYPE: return (double)arg->value.val_int; case FLOAT_TYPE: return (double)arg->value.val_float; case DOUBLE_TYPE: return arg->value.val_double; case BOOL_TYPE: return (double)arg->value.val_bool; default: fprintf(stderr, "Error: Cannot convert argument to double\n"); return 0.0; } } // 辅助函数:将value_info转换为int类型参数 int get_int_arg(value_info *arg) { switch(arg->type) { case INT_TYPE: return arg->value.val_int; case FLOAT_TYPE: return (int)arg->value.val_float; case DOUBLE_TYPE: return (int)arg->value.val_double; case BOOL_TYPE: return (int)arg->value.val_bool; default: fprintf(stderr, "Error: Cannot convert argument to int\n"); return 0; } } // 根据函数名查找函数表中的function_info function_info* find_function(const char *fname) { size_t fn_count = sizeof(arith_fncts) / sizeof(arith_fncts[0]); for(size_t i = 0; i < fn_count; i++) { if(strcmp(arith_fncts[i].fname, fname) == 0) { return &arith_fncts[i].fnct; } } fprintf(stderr, "Error: Function '%s' not found\n", fname); return NULL; } // 调用函数并返回结果(支持0-10个参数,这里实现了1个参数的示例,可扩展) value_info call_function(function_info *fn, value_info args_array[]) { value_info result = {.type = fn->ret_type}; switch(fn->argc) { // 0个参数的情况 case 0: switch(fn->ret_type) { case INT_TYPE: result.value.val_int = fn->function.f_int(); break; case DOUBLE_TYPE: result.value.val_double = fn->function.f_double(); break; default: fprintf(stderr, "Error: Unsupported return type for 0-arg function\n"); result.type = -1; } break; // 1个参数的情况 case 1: switch(fn->ret_type) { case DOUBLE_TYPE: if(fn->argv[0] == DOUBLE_TYPE) { result.value.val_double = fn->function.f_double(get_double_arg(&args_array[0])); } else if(fn->argv[0] == INT_TYPE) { result.value.val_double = ((double(*)(int))fn->function.f_double)(get_int_arg(&args_array[0])); } else { fprintf(stderr, "Error: Unsupported argument type for 1-arg function\n"); result.type = -1; } break; default: fprintf(stderr, "Error: Unsupported return type for 1-arg function\n"); result.type = -1; } break; // 扩展2-10个参数的分支逻辑,格式参考case 1 // case 2: ... // case 3: ... // ... // case 10: ... default: fprintf(stderr, "Error: Unsupported argument count (%d)\n", fn->argc); result.type = -1; break; } return result; } // 测试示例 int main() { // 准备参数:sin(π/2) value_info args_array[10]; args_array[0].type = DOUBLE_TYPE; args_array[0].value.val_double = M_PI / 2; function_info *fn = find_function("sin"); if(fn) { value_info res = call_function(fn, args_array); if(res.type == DOUBLE_TYPE) { printf("sin(π/2) = %.6f\n", res.value.val_double); } } return 0; }
关键细节说明
- 参数转换:通过
get_xxx_arg函数在运行时根据value_info的类型提取对应值并转换为函数期望的类型,解决了C静态类型无法动态匹配的问题。 - 函数指针转换:由于
function_info的functionunion存储的是无参数的函数指针,调用时必须转换为对应参数个数和类型的函数指针(例如(double(*)(int))fn->function.f_double)。 - 扩展性:若需要支持更多参数个数,只需在
call_function的switch中添加对应case,并实现该参数个数下的类型判断与调用逻辑即可。
内容的提问来源于stack exchange,提问作者holamynameisyes
相关产品推荐
相关产品推荐

