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

问题描述 投票:0回答:1

我正在编写一种名为

Trunk
的自定义 JIT(解释型)编程语言,目前我正在尝试从代码中解析函数并将它们存储在全局名称 -> 函数映射中。但我一直在思考
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 | };
      | ^
c++ function types function-pointers
1个回答
0
投票

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
实例化来检查它是否被赋予了它所持有的函数期望的参数数量和类型。

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

© www.soinside.com 2019 - 2024. All rights reserved.