在可变参数模板中派生(虚拟)函数参数 class

Derive (virtual) function arguments in variadic template class

我正在构建一个解释器,并试图避免在实现内置函数时 运行 使用一些样板文件。我可以使用模板来做到这一点。

以此基础模板为例:

template<ast::builtin_type T>
class builtin_procedure abstract : public builtin_procedure_symbol
{
    using arg_traits = builtin_type_traits<T>;

protected:
    builtin_procedure(const symbol_identifier& identifier): builtin_procedure_symbol(identifier)
    {
        this->register_param(arg_traits::param_id(), T);
    }

    /**
     * The actual implementation of the built-in function
     */
    virtual void invoke_impl(typename arg_traits::builtin_type) = 0;

public:
    void invoke(scope_context& procedure_scope) override
    {
        auto raw_arg = procedure_scope.memory->get(procedure_scope.symbols.get(arg_traits::param_id()));
        this->invoke_impl(arg_traits::get_from_expression(raw_arg));
    }
};

要实现一个带字符串的内置函数function,我只需要做:

class builtin_procedure_writeln final : public builtin_procedure<ast::builtin_type::string>
{
protected:
    void invoke_impl(arg_traits::builtin_type arg) override;

public:
    builtin_procedure_writeln();
}; /* Implementation in cpp file */

很方便,我只需要实现虚invoke_impl方法就可以了。

我正在努力用可变数量的模板参数来实现它,这样如果我想在我的派生中支持 2 个、3 个或更多参数,我就不必复制我的模板定义实施 如以下示例所示

这将是上面的模板,以支持第二个模板参数:

template<ast::builtin_type T1, ast::builtin_type T2>
class builtin_procedure abstract : public builtin_procedure_symbol
{
    using arg1_traits = builtin_type_traits<T1>;
    using arg2_traits = builtin_type_traits<T2>;

protected:
    builtin_procedure(const symbol_identifier& identifier): builtin_procedure_symbol(identifier)
    {
        this->register_param(arg_traits::param_id(1), T1);
        this->register_param(arg_traits::param_id(2), T2);
    }

    /**
     * The actual implementation of the built-in function
     */
    virtual void invoke_impl(typename arg1_traits::builtin_type, typename arg2_traits::builtin_type) = 0;

public:
    void invoke(scope_context& procedure_scope) override
    {
        auto raw_arg1 = procedure_scope.memory->get(procedure_scope.symbols.get(arg1_traits::param_id()));
        auto raw_arg2 = procedure_scope.memory->get(procedure_scope.symbols.get(arg2_traits::param_id()));
        this->invoke_impl(arg1_traits::get_from_expression(raw_arg1), arg2_traits::get_from_expression(raw_arg2));
    }
};

我知道基本上通过模板递归你可以遍历每个模板参数来做任何你想做的事,但是虚拟 invoke_impl 方法的定义呢?每个参数都派生自 traits 结构,对方法本身的调用似乎也不是模板递归可以做到的。

如何(如果)使用可变参数模板允许在此基础 class 上使用可变数量的参数作为仅 copy/paste 此基础 class 的替代方案更多模板参数?


最后的线索给了n314159,这个有效:

template<ast::builtin_type... Ts>
class builtin_procedure abstract : public builtin_procedure_symbol
{
private:
    template<ast::builtin_type T>
    typename builtin_type_traits<T>::builtin_type make_arg(scope_context& procedure_scope, int param_id)
    {
        auto raw_arg = procedure_scope.memory->get(procedure_scope.symbols.get(builtin_type_traits<T>::param_id(param_id++)));
        return builtin_type_traits<T>::get_from_expression(raw_arg);
    }

protected:
    builtin_procedure(const symbol_identifier& identifier, ::symbol_table* runtime_symbol_table): builtin_procedure_symbol(identifier, runtime_symbol_table)
    {
        auto param_id = 0;

        ((void) this->register_param(builtin_type_traits<Ts>::param_id(++param_id), Ts), ...);
    }

    virtual void invoke_impl(typename builtin_type_traits<Ts>::builtin_type...) = 0;

public:
    void invoke(scope_context& procedure_scope) override
    {
        auto param_id = 0;
        this->invoke_impl(make_arg<Ts>(procedure_scope, ++param_id)...);
    }
};

所以,我写了一个小例子。我不认为有人可以为可变参数模板做别名,所以我把它留了下来,但即使它不那么好,它也能正常工作。所以,由于我不能使用非整数非类型模板参数,我将你的 ast::builtin_type 切换为 int,但我认为你可以很容易地反转它。以下编译(但不link,显然^^)。

template<int i>
struct builtin_traits {
    static int param_id(int) { return i;}
    using builtin_type = int;
};

class builtin_procedure_symbol {
    void register_param(int, int);
};

int get(int); // my replacement for procedure_scope.memory->get(procedure_scope.symbols.get

template<int... Ts>
class builtin_procedure : builtin_procedure_symbol{
    builtin_procedure(): builtin_procedure_symbol()
    {
        ((void) this->register_param(builtin_traits<Ts>::param_id(1), Ts), ... );
    }

    virtual void invoke_impl(typename builtin_traits<Ts>::builtin_type...) = 0;

    void invoke() 
    {
        auto f = [&](const auto& arg) {
            auto raw_arg = get(builtin_traits<arg>::param_id());
            return builtin_traits<arg>::get_from_expression(raw_arg);
        };
        this->invoke_impl(f(Ts)...);
    }
};

希望对你有所帮助。如有不明之处请追问。