存储指向具有任何返回类型和任何数字或参数的函数的指针

Storing a pointer to a function with any return type and any number or arguments

提问人:TheEagle 提问时间:10/20/2023 最后编辑:TheEagle 更新时间:10/20/2023 访问量:87

问:

我正在编写一种名为 的自定义 JIT(解释型)编程语言,目前我正在尝试从代码中解析函数并将它们存储在全局名称 -> 函数映射中。但是我被困在弄清楚要为的第二个模板参数使用什么类型。这是我到目前为止所拥有的:Trunkstd::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 | };
      | ^
C++ 类型 函数指针

评论

3赞 Miles Budnek 10/20/2023
trunk::Function不是一个类型,所以你不能有它们的地图。您需要重新考虑您的设计,以避免使用异构函数映射,或者对函数可调用对象进行类型擦除并对其签名进行运行时检查。
1赞 YurkoFlisk 10/20/2023
一个编译器错误是来自不正确的声明语法,它应该是operator()Ret operator()(Args... args)
0赞 273K 10/20/2023
什么运营商?我想? 只是一个关键字,后跟运算符名称和运算符参数。operator()operator()
0赞 YurkoFlisk 10/20/2023
至于主要问题,我认为您应该具有非模板化并存储有关其参数、返回类型等的信息作为数据成员。您的语言中的类型(看起来是解释的)不一定是 C++ 代码中的类型,毕竟您在解释期间在运行时读取它们。使用一些参数调用 a,从您的语言中的代码解释,然后将在运行时检查签名。FunctionFunction
0赞 TheEagle 10/20/2023
@237K啊,是的,忽略那对额外的括号是多么容易......西德

答:

1赞 Miles Budnek 10/20/2023 #1

trunk::Function不是一个类型;它是一个模板:一个蓝图,可以从中创建任意数量的不同类型。例如,是与 不同的类型。trunk::Function<void, int>trunk::Function<int, double>

由于没有单一类型,因此无法创建 .trunk::Functionstd::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_;
    };
}

演示

基本思想是接受任意数量的任何类型的参数,然后使用多态的、类型擦除的实例化来检查它是否被赋予了它所持有的函数所期望的参数的数量和类型。FunctionFunctionImpl

请注意,此实现有一些缺点。即围绕参考参数。它旨在展示您可以采取的可能方法,而不仅仅是一个完整的解决方案。