C语言中实现可调用任意函数的调用器及Telnet远程调用方案问询
问题描述
我有一批参数数量、类型和返回值类型均不固定的函数,示例如下:
void func1 (void arg1, int arg2); int func2 (void arg1, int arg2, char* arg3); char func3 (char* arg1, int arg2, FILE* arg3); // ... 更多函数
我想实现一个能接收这类函数作为参数,并返回其执行结果的调用函数,大致思路如下:
void* func_caller(func* func(args)) { return func; }
具体应用场景是通过Telnet套接字调用这些函数:我已经完成了连接建立的代码,希望能在Telnet提示符下输入指令调用程序内的函数,例如:
-> add(4,5) 或 -> add 4 5 /* 输入格式不限 */ Ans: 9 -> read_log_file() Ans: reading File....
请问该如何实现?
实现方案
C语言本身不支持原生的可变参数函数指针泛型调用,需通过函数注册表维护、输入解析、类型转换三个核心步骤实现,具体如下:
1. 定义函数元数据结构
为每个可调用函数维护元数据,记录函数名、参数数量、参数类型、返回值类型及函数指针:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdint.h> // 参数类型枚举 typedef enum { ARG_INT, ARG_STRING, ARG_FILE, ARG_VOID } ArgType; // 返回值类型枚举 typedef enum { RET_VOID, RET_INT, RET_CHAR, RET_STRING } RetType; // 函数注册表项结构 typedef struct { const char* name; int arg_count; ArgType* arg_types; RetType ret_type; void* func_ptr; } FuncRegistryItem;
2. 构建函数注册表
将所有允许Telnet调用的函数注册到全局数组中:
// 示例函数:对应add(4,5) int add(int a, int b) { return a + b; } // 示例函数:对应read_log_file() void read_log_file() { printf("reading File....\n"); } // 定义参数类型数组 ArgType add_args[] = {ARG_INT, ARG_INT}; ArgType read_log_args[] = {}; // 函数注册表 FuncRegistryItem func_registry[] = { {"add", 2, add_args, RET_INT, (void*)add}, {"read_log_file", 0, read_log_args, RET_VOID, (void*)read_log_file}, // 可继续添加更多函数 }; #define REGISTRY_SIZE (sizeof(func_registry) / sizeof(FuncRegistryItem))
3. 解析Telnet输入
从输入字符串中提取函数名和参数列表,兼容add(4,5)和add 4 5两种格式:
// 解析输入,提取函数名和参数字符串数组 int parse_input(const char* input, char** func_name, char*** args, int* arg_count) { char* input_copy = strdup(input); if (!input_copy) return -1; // 处理带括号的格式:将add(4,5)转换为add 4 5 char* paren_start = strchr(input_copy, '('); if (paren_start) { *paren_start = ' '; char* paren_end = strchr(input_copy, ')'); if (paren_end) *paren_end = '\0'; // 替换逗号为空格 char* comma; while ((comma = strchr(input_copy, ',')) != NULL) { *comma = ' '; } } // 按空格分割字符串 char* token = strtok(input_copy, " "); if (!token) { free(input_copy); return -1; } *func_name = strdup(token); *arg_count = 0; *args = NULL; while ((token = strtok(NULL, " ")) != NULL) { *args = realloc(*args, (*arg_count + 1) * sizeof(char*)); (*args)[*arg_count] = strdup(token); (*arg_count)++; } free(input_copy); return 0; }
4. 类型转换与函数调用
根据注册表元数据转换参数类型,调用目标函数并处理返回值:
// 执行函数调用并输出结果 void execute_func(const char* func_name, char** args, int arg_count) { // 在注册表中查找函数 FuncRegistryItem* item = NULL; for (int i = 0; i < REGISTRY_SIZE; i++) { if (strcmp(func_registry[i].name, func_name) == 0) { item = &func_registry[i]; break; } } if (!item) { printf("Ans: Unknown function '%s'\n", func_name); return; } // 检查参数数量是否匹配 if (item->arg_count != arg_count) { printf("Ans: Wrong argument count for '%s', expected %d, got %d\n", func_name, item->arg_count, arg_count); return; } // 转换参数类型(示例仅实现部分类型,可扩展) void* converted_args[10]; // 假设最多支持10个参数,可调整 for (int i = 0; i < arg_count; i++) { switch (item->arg_types[i]) { case ARG_INT: converted_args[i] = (void*)(intptr_t)atoi(args[i]); break; case ARG_STRING: converted_args[i] = args[i]; break; case ARG_FILE: converted_args[i] = fopen(args[i], "r"); if (!converted_args[i]) { printf("Ans: Failed to open file '%s'\n", args[i]); return; } break; default: printf("Ans: Unsupported argument type\n"); return; } } // 根据返回值类型调用函数 switch (item->ret_type) { case RET_VOID: switch (item->arg_count) { case 0: ((void(*)())item->func_ptr)(); break; case 2: ((void(*)(int, int))item->func_ptr)((int)(intptr_t)converted_args[0], (int)(intptr_t)converted_args[1]); break; default: printf("Ans: Unsupported argument count for void function\n"); return; } printf("Ans: executed successfully\n"); break; case RET_INT: int int_ret; switch (item->arg_count) { case 2: int_ret = ((int(*)(int, int))item->func_ptr)((int)(intptr_t)converted_args[0], (int)(intptr_t)converted_args[1]); break; default: printf("Ans: Unsupported argument count for int function\n"); return; } printf("Ans: %d\n", int_ret); break; // 可扩展RET_CHAR、RET_STRING等类型的处理逻辑 default: printf("Ans: Unsupported return type\n"); return; } // 清理资源(如打开的文件) for (int i = 0; i < arg_count; i++) { if (item->arg_types[i] == ARG_FILE && converted_args[i]) { fclose((FILE*)converted_args[i]); } } }
5. 整合Telnet交互逻辑
将上述模块与已有的Telnet连接代码整合,实现循环接收输入、解析、执行的逻辑:
int main() { // 此处替换为实际Telnet连接的输入输出流/套接字FD // 示例用标准输入模拟Telnet交互 char input_buf[256]; printf("-> "); while (fgets(input_buf, sizeof(input_buf), stdin) != NULL) { // 去掉换行符 input_buf[strcspn(input_buf, "\n")] = '\0'; if (strlen(input_buf) == 0) { printf("-> "); continue; } char* func_name = NULL; char** args = NULL; int arg_count = 0; if (parse_input(input_buf, &func_name, &args, &arg_count) == 0) { execute_func(func_name, args, arg_count); // 释放解析资源 free(func_name); for (int i = 0; i < arg_count; i++) { free(args[i]); } free(args); } else { printf("Ans: Invalid input format\n"); } printf("-> "); } return 0; }
注意事项
- 上述为基础框架,需根据实际函数类型扩展参数/返回值的处理分支
- 需严格管理动态分配的内存,避免泄漏
- 对于FILE*等资源,调用后必须及时释放
- 若需支持带引号的字符串参数,需完善输入解析逻辑
- 多连接场景下需考虑线程安全,添加同步机制
内容的提问来源于stack exchange,提问作者Ahmad Ibrahem
相关产品推荐
相关产品推荐

