Search code examples
c++c++17template-meta-programmingparameter-pack

C++ iterate template parameters


It's possible to fill the appropriate registers of a virtual machine based on the argument list and some logic using a C++17 fold, like so: https://github.com/fwsGonzo/libriscv/blob/master/lib/libriscv/machine_vmcall.hpp#L35

https://github.com/fwsGonzo/libriscv/blob/master/lib/libriscv/machine_vmcall.hpp#L18

Structs will be pushed onto the stack and the address will take up an integer register slot. So, I can turn a regular function call into a call into my virtual machine. I don't think any other programming language can do that.

Now, for the other way around there are system call handlers. They look like this: https://github.com/fwsGonzo/libriscv/blob/master/emulator/src/syscalls.cpp#L20

In an effort to simplify system call handling I want to be able to take a list of argument types, perform some logic on each of them (extract the values based on type), and then optionally call a lambda with the arguments I built up.

The number and types of arguments is known beforehand. The values are not known until they are extracted from the machine registers.

Thanks to @bipll for answering. I chose to implement it like this:

template<typename... Args, std::size_t... indices>
inline auto resolve_args(machine_t& m, std::index_sequence<indices...>)
{
    std::tuple<std::decay_t<Args>...> retval;
    ((std::get<indices>(retval) = m.template sysarg<Args>(indices)), ...);
    return retval;
}
template<typename... Args>
inline auto resolve_args(machine_t& m) {
    return resolve_args<Args...>(m, std::index_sequence_for<Args...>{});
}

Solution

  • Hmm, is this something you're after?

    #include <iostream>
    #include <type_traits>
    
    template<class... Args> void omg(Args &&...args)
    {
            ((std::is_integral_v<Args> && std::cout << args << " is integral\n"), ...);
    }
    
    int main() {
            omg(1, 42, 3.14, "Hi!", 0<<0);
    }
    
    1 is integral
    42 is integral
    0 is integral
    

    operator, could be the Swiss army knife of unary foreach fold-expressions.

    You don't even need actual values for that:

    template<class... Args> void omg()
    {
            std::size_t i{};
            ((++i,
              std::is_integral_v<Args> && std::cout <<
                      "Arg #" << i << " is integral\n"
              || std::is_scalar_v<Args> && std::cout <<
                      "Arg #" << i << " is not integral, yet is scalar\n"
              ), ...);
    }
    
    int main() {
            omg<int, int, double, char const *, std::size_t>();
    }
    

    If you don't have actual values at hand, but their types and index access, well, you can easily retrieve them through some very basic way:

    template<class... Args, std::size_t... indices> void add_args(
            std::index_sequence<indices...>)
    {
        (p.append(sys.get_arg<Args>(indices)), ...);
    }
    
    template<class... Args> void add_args() {
        add_args<Args...>(std::index_sequence_for<Args...>{});
    }
    

    Even storing them in a tuple is a bit tricky and not quite straightforward:

    std::tuple<std::decay_t<Args>...> retval;
    ((std::get<indices>(retval) = sys.get_arg<Args>(indices)), ...);
    return retval;