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

通用函数指针存储与调用:Caller类Invoke方法实现疑问

解决方案

问题1:存储不同签名的函数指针

要统一存储不同签名的函数指针,核心思路是类型擦除——把不同类型的函数包装成统一的可存储类型。这里推荐两种实用方案:

方案一:用std::any+std::function实现

借助C++17的std::any实现类型擦除,配合std::function包装函数,用哈希表存储函数名与包装后对象的映射:

#include <unordered_map>
#include <any>
#include <functional>
#include <vector>
#include <stdexcept>

class Caller {
private:
    std::unordered_map<std::string, std::any> func_table;
public:
    // 模板注册方法,支持任意void返回值的函数
    template<typename Func>
    void Register(const std::string& name, Func func) {
        func_table[name] = std::function(std::move(func));
    }

    // 可变模板参数版Invoke,替代C风格可变参数
    template<typename... Args>
    void Invoke(const std::string& function_name, Args&&... args) {
        auto it = func_table.find(function_name);
        if (it == func_table.end()) {
            throw std::runtime_error("Function not found: " + function_name);
        }

        try {
            // 将std::any转换为对应签名的std::function,再调用
            std::invoke(
                std::any_cast<std::function<void(Args...)>>(it->second),
                std::forward<Args>(args)...
            );
        } catch (const std::bad_any_cast& e) {
            throw std::runtime_error("Parameter mismatch for function: " + function_name);
        }
    }
};

方案二:自定义抽象基类实现

如果不能使用C++17的std::any,可以定义抽象基类作为统一接口,为每个函数签名创建派生类存储函数指针:

#include <unordered_map>
#include <vector>
#include <stdexcept>

class FunctionWrapper {
public:
    virtual ~FunctionWrapper() = default;
};

template<typename Func>
class ConcreteFunction : public FunctionWrapper {
private:
    Func func;
public:
    ConcreteFunction(Func f) : func(std::move(f)) {}
    Func& GetFunc() { return func; }
};

class Caller {
private:
    std::unordered_map<std::string, FunctionWrapper*> func_table;
public:
    ~Caller() {
        for (auto& pair : func_table) {
            delete pair.second;
        }
    }

    template<typename Func>
    void Register(const std::string& name, Func func) {
        func_table[name] = new ConcreteFunction<Func>(std::move(func));
    }

    template<typename... Args>
    void Invoke(const std::string& function_name, Args&&... args) {
        auto it = func_table.find(function_name);
        if (it == func_table.end()) {
            throw std::runtime_error("Function not found: " + function_name);
        }

        auto wrapper = dynamic_cast<ConcreteFunction<std::function<void(Args...)>>*>(it->second);
        if (!wrapper) {
            throw std::runtime_error("Parameter mismatch for function: " + function_name);
        }

        std::invoke(wrapper->GetFunc(), std::forward<Args>(args)...);
    }
};

问题2:Invoke传递参数的实现

C风格的可变参数(...)缺乏类型安全,且无法在编译期获取参数信息,强烈推荐使用C++11引入的可变模板参数实现Invoke:

  1. 用template<typename... Args>定义参数包,接收任意数量、类型的参数;
  2. 通过std::forward<Args>(args)...完美转发参数,减少不必要的拷贝;
  3. 借助std::invoke调用函数,它能兼容普通函数、Lambda、成员函数等多种可调用对象。

完整使用示例

// 目标函数
void doSomething(int arg) { /* 实现逻辑 */ }
void doSomethingElse(int x, float y, const std::vector<int>& z) { /* 实现逻辑 */ }

// 调用示例
void MyFunc(Caller* caller, const std::vector<int>& data) {
    caller->Invoke("doSomething", 10);
    caller->Invoke("doSomethingElse", 10, 1.0f, data);
}

int main() {
    Caller caller;
    // 注册函数
    caller.Register("doSomething", &doSomething);
    caller.Register("doSomethingElse", &doSomethingElse);

    std::vector<int> data = {1, 2, 3};
    MyFunc(&caller, data);
    return 0;
}

不推荐的C风格可变参数方案

如果必须使用C风格...,只能硬编码每个函数的参数解析逻辑,这种方式类型不安全且扩展性极差:

void Invoke(const char* function_name, ...) {
    va_list args;
    va_start(args, function_name);

    if (strcmp(function_name, "doSomething") == 0) {
        int arg = va_arg(args, int);
        doSomething(arg);
    } else if (strcmp(function_name, "doSomethingElse") == 0) {
        int x = va_arg(args, int);
        float y = va_arg(args, float);
        // 引用类型会退化为指针,需手动转换,极易出错
        const std::vector<int>* z_ptr = va_arg(args, const std::vector<int>*);
        doSomethingElse(x, y, *z_ptr);
    }

    va_end(args);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 17:53:10