1

当前(旧)函数调用签名:

        long vmcall(const std::string& function_name,
                    std::vector<address_t> args);

现在,由于我只能使用(地址大小的)整数调用来宾,这是一个非常有限的接口,我想将其扩展为至少也处理浮点值,可能还有其他值,如下所示:

        template <typename... Args>
        long vmcall(const std::string& function_name,
                    Args&&... args);

那将是新的函数签名,取代旧的。

要显示当前如何使用 args 向量:

template <int W>
inline void Machine<W>::setup_call(
        address_t call_addr, address_t retn_addr,
        std::vector<address_t> args)
{
    assert(args.size() <= 8);
    cpu.reg(RISCV::REG_RA) = retn_addr;
    for (size_t arg = 0; arg < args.size(); arg++) {
        cpu.reg(RISCV::REG_ARG0 + arg) = args[arg];
    }
    cpu.jump(call_addr);
}

这个函数是用 std::move(args) 从 vmc​​all 调用的,它所做的只是将 args 向量中的整数参数以增量方式复制到整数 CPU 寄存器中。当我想调用一个只接受整数参数的函数时,这很好用。所以,如果我想调用一个函数,比如说,它接受一个浮点参数,那么就没有办法做到这一点。这是因为 FP 寄存器是完全独立的,需要区别对待。

此外,32 位和 64 位浮点数通过 NaN 装箱以不同方式处理。所以,如果能区分 float 和 double 就好了。

一开始我对模板魔法的能力并不强。如何对参数包中每个元素的类型进行分支?

4

1 回答 1

4

在 C++17 中,迭代参数包的方法是使用逗号折叠

(<expr>, ...); // expr contains Args and/or args

如果单个表达式不够用,您可以使用立即调用的 lambda:

([&] {
    <statements> // statements contains Args and/or args
 }(), ...);

将此与if constexpr您的论点类型结合起来:

std::vector<address_t> addr_args;
std::vector<fp_t> fp_args;
([&] {
    if constexpr (std::is_integral_v<Args>)
        addr_args.push_back(args);
    else
        fp_args.push_back(args);
 }(), ...);
于 2020-02-28T21:58:41.490 回答