提问人:TheEagle 提问时间:10/20/2023 最后编辑:TheEagle 更新时间:10/20/2023 访问量:87
存储指向具有任何返回类型和任何数字或参数的函数的指针
Storing a pointer to a function with any return type and any number or arguments
问:
我正在编写一种名为 的自定义 JIT(解释型)编程语言,目前我正在尝试从代码中解析函数并将它们存储在全局名称 -> 函数映射中。但是我被困在弄清楚要为的第二个模板参数使用什么类型。这是我到目前为止所拥有的:Trunk
std::map
namespace trunk {
template<typename Ret, typename... Args>
class Function {
using FunctionType = Ret (*)(Args...);
public:
Function(FunctionType callable): _callable(callable) {}
~Function() {}
Ret operator(Args... args) {
return _callable(args...);
}
protected:
FunctionType _callable;
};
namespace globals {
void print(std::string s) {
std::cout << s << std::endl;
}
}
}
static trunk::Function print_function = trunk::Function(&trunk::globals::print);
static std::map< std::string, trunk::Function > function_map = {
{"print", &print_function},
};
但这会引发以下错误:
In file included from /media/frederic/WD-5TB/.fg/Programme/trunk/src/main.cxx:5:
/media/frederic/WD-5TB/.fg/Programme/trunk/include/trunk/function.hxx:15:37: error: expected type-specifier before ‘(’ token
15 | Ret operator(Args... args) {
| ^
/media/frederic/WD-5TB/.fg/Programme/trunk/src/main.cxx:16:47: error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Key, class _Tp, class _Compare, class _Alloc> class std::map’
16 | static std::map< std::string, trunk::Function > function_map = {
| ^
/media/frederic/WD-5TB/.fg/Programme/trunk/src/main.cxx:16:47: note: expected a type, got ‘Function’
/media/frederic/WD-5TB/.fg/Programme/trunk/src/main.cxx:16:47: error: template argument 4 is invalid
/media/frederic/WD-5TB/.fg/Programme/trunk/src/main.cxx:18:1: error: too many braces around scalar initializer for type ‘int’
18 | };
| ^
答:
1赞
Miles Budnek
10/20/2023
#1
trunk::Function
不是一个类型;它是一个模板:一个蓝图,可以从中创建任意数量的不同类型。例如,是与 不同的类型。trunk::Function<void, int>
trunk::Function<int, double>
由于没有单一类型,因此无法创建 .trunk::Function
std::map<std::string, trunk::Function>
如果要拥有所有函数的单个异构映射,则需要围绕它们创建某种类型擦除包装器,并在运行时检查参数的数量和类型以及返回值的类型。
下面是一个相当基本的示例:
namespace trunk {
class FunctionImplBase
{
public:
virtual std::any call(const std::vector<std::any>& args) = 0;
};
template <typename Ret, typename... Args>
class FunctionImpl : public FunctionImplBase
{
private:
using FunctionType = Ret (*)(Args...);
FunctionType callable_;
public:
FunctionImpl(FunctionType callable) : callable_{callable} {}
std::any call(const std::vector<std::any>& args) override
{
if (args.size() != sizeof...(Args)) {
throw std::runtime_error("Wrong number of arguments supplied");
}
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
if constexpr (std::is_same_v<Ret, void>) {
callable_(std::any_cast<Args>(args[Is])...);
return std::any{};
} else {
return callable_(std::any_cast<Args>(args[Is])...);
}
}(std::make_index_sequence<sizeof...(Args)>{});
}
};
class Function
{
public:
Function() {}
template <typename Ret, typename... Args>
Function(Ret(*callable)(Args...))
: impl_{std::make_shared<FunctionImpl<Ret, Args...>>(callable)}
{}
std::any operator()(const std::vector<std::any>& args) const {
return impl_->call(args);
}
private:
std::shared_ptr<FunctionImplBase> impl_;
};
}
基本思想是接受任意数量的任何类型的参数,然后使用多态的、类型擦除的实例化来检查它是否被赋予了它所持有的函数所期望的参数的数量和类型。Function
FunctionImpl
请注意,此实现有一些缺点。即围绕参考参数。它旨在展示您可以采取的可能方法,而不仅仅是一个完整的解决方案。
评论
trunk::Function
不是一个类型,所以你不能有它们的地图。您需要重新考虑您的设计,以避免使用异构函数映射,或者对函数可调用对象进行类型擦除并对其签名进行运行时检查。operator()
Ret operator()(Args... args)
operator()
operator
()
Function
Function