bounty将在3天后过期。回答此问题可获得+50的声誉奖励。joergbrech正在寻找来自声誉良好来源的答案。
我有一个框架,它有一个类型擦除的函数类型Function
,它将std::any
示例Map到std::any
示例和一个函数注册表std::unordered_map<std::string, Function>
。这个框架有一个插件系统,插件作者可以在这个函数注册表中注册函数。这样,新函数就可以在运行时集成到框架中。
该框架的一个非常简化的实现可能如下所示:
#include <any>
#include <vector>
#include <memory>
#include <functional>
#include <iostream>
class Function
{
public:
template <typename R, typename... Args>
Function(R(*f)(Args...)) : impl(std::make_unique<FunctionImpl<R, Args...>>(f)) {}
template <typename... Args>
std::any operator()(Args&&...args) const
{
std::vector<std::any> args_vec({std::forward<Args>(args)...});
return impl->invoke(args_vec);
}
private:
struct FunctionProxy {
virtual std::any invoke(std::vector<std::any>&) const = 0;
virtual ~FunctionProxy(){}
};
template <typename R, typename... Args>
class FunctionImpl : public FunctionProxy
{
public:
FunctionImpl(R(*f)(Args...)) : fun{f} {}
std::any invoke(std::vector<std::any>& args) const override
{
return invoke_impl(args, std::make_index_sequence<sizeof...(Args)>{});
}
private:
template <size_t... I>
std::any invoke_impl(std::vector<std::any>& args, std::index_sequence<I...>) const
{
if constexpr (std::is_void_v<R>) {
fun(std::any_cast<Args>(args[I])...);
return std::any{};
} else {
return std::any(fun(std::any_cast<Args>(args[I])...));
}
}
std::function<R(Args...)> fun;
};
std::unique_ptr<FunctionProxy> impl;
};
class FunctionRegistry
{
public:
template <typename R, typename... Args>
void register_function(R(*fun)(Args...), std::string name)
{
functions.emplace(std::make_pair(name, Function(fun)));
}
Function const& resolve(std::string const& name){
return functions.at(name);
}
private:
std::unordered_map<std::string, Function> functions;
};
/***********************************************************************************/
double add(double x, double y) {
return x+y;
}
template <typename... Args>
double sum(Args... args) {
return (0 + ... + args);
}
int main()
{
// code called in void init() function provided by plugin
FunctionRegistry functions;
functions.register_function(&add, "add");
//....
// code called in the framework at runtime
auto const& f = functions.resolve("add");
std::cout << std::any_cast<double>(f(5.,3.));
return 0;
}
https://godbolt.org/z/aT4n36v6M
注意,为了解决这个问题,这个例子被简化了 (没有错误检查,真实的的代码没有使用std::any
,而是使用了一个适当的反射库,它不限于函数指针和std::function,main函数的第一部分将作为插件的一部分来实现......)
代码可以很好地处理非模板函数。但是现在我想让用户在插件中提供变量函数。我意识到在当前的设置中这是不可能的,因为模板是在调用点初始化的,而不是作为插件的一部分编译的。
有没有办法让用户在这种框架中注册可变变量函数?
我对所有的解决方案都是开放的,只要现有的功能保持不变,包括API在内的代码的任何部分都可以修改。
唯一的要求是,注册的函数是 truely variadic:我知道我可以在插件中显式地示例化变量函数的1,2,3,......,n个参数,并分别注册每个示例化。
1条答案
按热度按时间j7dteeu81#
正如问题中所述,对于
template<typename ...T> void for(T...);
可变参数函数(或一般情况下-任何函数模板),您可以注册它的任何示例-例如fwk.register("foo_ifi", &foo<int,float,int>);
这就是我们所能做的,除非问题是不提供模板参数,否则你可以使用lambda来 Package 你的函数模板:
固定参数数的解决方案:
如果你能限制你的框架的参数和类型的最大数量--那么这是可行的--参见an example。
只有几个助手类:
而框架本身--通过一些模板魔术、元组、变量、函数和lambda--几乎可以自动完成:
它的作用证明:
任意数量参数的解:
如果你想注册这个函数模板本身,使它可以被任何参数调用-你需要使框架也成为一个知道它的“函数”的模板。
为了使注册变得简单一些,需要一些帮助类:
和一些例子-如何使用它:
正如你所看到的--这样的变量需要被包含在lambda中--因为只有lambda是我们可以提供的非模板的东西--然而它的调用操作符是一个方法模板。
以及它的工作原理: