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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 22:20:36