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...>{});
}
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;