【问题标题】:How to call a generic std::function with a list of void* parameters?如何使用 void* 参数列表调用通用 std::function?
【发布时间】:2020-06-18 07:53:24
【问题描述】:

我有一个 std::map 存储一些我想稍后调用的任意方法。

map<string, function<void(void)>> methods;

template<typename R, typename ...P>
void storeMethod(const string& name, const function<R(P...)>& method) {
    methods[name] = method;
}    

在调用时,我在vector&lt;void*&gt; 中获取调用方法的参数,其中第一个元素是我将存储返回值的指针。
如何自动将这些参数转换为我要调用的方法的相应类型?
我应该以某种方式存储类型吗?

void callMethod(const string& name, vector<void*> parameters) {
    auto method = methods[name];
    // How to call 'method' with all the parameters casted to the required types?
}

例如,如果我最初用function&lt;int(string a, float b)&gt; 调用storeMethod(),我需要一个通用的方式在callMethod() 中调用它,如下所示:

*((int*)parameters[0]) = method(*((string*)parameters[1]), *((float*)parameters[2]));

【问题讨论】:

  • 为什么你得到一个参数为vector&lt;void*&gt;?当涉及到 C 时,有时需要使用 void*,但 vector&lt;void*&gt; 不是您需要的东西
  • std::function&lt;void(void)&gt; 可以存储一个不接受任何参数且不返回任何内容的函数(并用类型擦除对其进行包装)。您不能存储在接受参数或返回的函数中。
  • 在 C++ 中,您应该使用模板和参数包而不是这种奇怪的std::vector&lt;void*&gt; 以通用方式转发参数。
  • "void* 因为我正在处理 C 库" ... 好的,但是 vector&lt;void*&gt; 不能来自 C 库。你怎么知道实际的类型是什么?
  • 来自该 C 库的可能类型集是否仅限于某个预定义的子集?

标签: c++ templates generics


【解决方案1】:

您必须将method 包装在可以记住参数类型的东西中。

struct OpaqueFunction {
    virtual std::any call(const std::vector<std::any> &) = 0;
};

template <typename R, typename ... Args>
struct OpaqueFunctionImpl : OpaqueFunction {
    OpaqueFunctionImpl(std::function<R(Args...)> f) : f(std::move(f)) {}

    std::any call(const std::vector<std::any> & parameters) override {
        return call_impl(parameters, std::index_sequence_for<Args...>{});
    }
private:
    template <size_t... I>
    std::any call_impl(const std::vector<std::any> & parameters, std::index_sequence<I...>) {
        return f(std::any_cast<Args>(parameters.at(I))...);
    }
    std::function<R(Args...)> f;
};

class Methods {
    std::map<std::string, std::unique_ptr<OpaqueFunction>> methods;
public:
    template<typename R, typename ... Args>
    void storeMethod(std::string name, std::function<R(Args...)> method) {
        methods[std::move(name)] = std::make_unique<OpaqueFunctionImpl<R, Args...>>(std::move(method));
    }

    template<typename R>
    R callMethod(const std::string & name, const std::vector<std::any> & parameters) {
        return std::any_cast<R>(methods.at(name)->call(parameters));
    }
};

【讨论】:

    【解决方案2】:

    您可以为接受不同类型参数的函数创建 Callable 类型,然后将其存储在 vector 中(为简单起见,此处使用向量):

    struct Callable {
        Callable(std::function<void()> f) : zero_(std::move(f)) {}
        Callable(std::function<void(int)> f) : one_(std::move(f)) {}
    
        void operator()() { zero_(); }
        void operator()(int x) { one_(x); }
    
        std::function<void()> zero_;
        std::function<void(int)> one_;
    };
    
    //vector of methods
    std::vector<Callable> methods;
    

    要存储方法,您可以使用template-ized 函数或仅使用重载。我在这里使用重载:

    void addMethod(std::function<void()> func)
    {
        methods.push_back(Callable(func));
    }
    
    void addMethod(std::function<void(int)> func)
    {
        methods.push_back(Callable(func));
    }
    

    然后最后调用一个函数:

    template<typename ...Args>
    void callMethod(int idx, Args ...args) {
        auto method = methods[idx];
    
        method(std::forward<Args>(args)...);
     }
    

    主要:

    int main()
    {
        addMethod([](int x){
            std::cout << "I am function(int)" << x << '\n';
        });
    
        addMethod([](){
            std::cout << "I am just function()\n";
        });
    
    
        callMethod(0, 200);
        callMethod(1);
    }
    

    这是我能想到的最简单的实现方式。他们可能是更好的方法,我真的很好奇。

    在这里试试:https://godbolt.org/z/HS5a7p

    【讨论】:

    • 每个可能的函数签名都需要一个成员
    • 恐怕是真的。
    • 而且,这并不能解决如何从 vector&lt;void*&gt; 检索参数的问题。
    • 很有趣,但参数将在 void* 的列表/向量中传递给 callMethod()。我应该如何适应这个?
    猜你喜欢
    • 2012-03-09
    • 2018-04-20
    • 2023-02-07
    • 1970-01-01
    • 2016-05-07
    • 2015-06-08
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多