I was wondering whether given the below constraints, it is possible to use variadic functions/templates in order to pass a variable number of parameters (which are themselves a return value of a function) into a variadic function.
using fn = F(...);
fn* func = (fn*) addr;
value = (*func)(pop_t(outputstack,o_stackidx,o_stacksize)...);
push_t(outputstack,o_stackidx,o_stacksize, value);
fn*
is a function pointer (converted from a known user-defined memory address of a function) which takes in a variable number of pop_t arguments (values poped from a stack), these values (outputstack,o_stackidx,o_stacksize) are static and do not need to be variadic themselves. Essentially I was wondering whether it's possible to have the pop_t function repeat a variable number of times either A.) by depending on the appropriate number of arguments fn
is capable of taking in or B.) using a user-defined integer specifying the number of repeats.
As an example say the user were to input a sin or atan2 function, these two functions take different numbers of parameters respectively being sin(x) and atan(y,x). For these two respective functions the function call expressions would look like the following:
sin -> (*func)(pop_t(outputstack,o_stackidx,o_stacksize));
atan2 -> (*func)(pop_t(outputstack,o_stackidx,o_stacksize),pop_t(outputstack,o_stackidx,o_stacksize));
Functions with N arguments pop N values from the stack by calling pop_t
N times.
template<class U, class I>
U pop_t(U* stack, I &stackidx, I &stacksize) {
if(stacksize>0) {
stacksize--;
stackidx = stackidx--;
return stack[stackidx];
}
else {
return stack[stackidx];
}
}
int main() {
float outputstack[2] = {3.141/2,1};
int o_stackidx = 2;
int o_stacksize = 2;
long addr = (long)&atan2;
using fn = float(...);
fn* func = (fn*) addr;
// Unknown variadic function pointer
float value = (*func)(pop_t(outputstack,o_stackidx,o_stacksize,nt)...);
return 0;
}
It seems that you want to repeat a statement multiple times based on the number of parameters, in that case, you can take help of C++ templates:
#include <cstddef>
#include <utility>
template <size_t N>
struct repeater {
template <typename F, typename ...Args>
static void do_work(F&& f, Args&&... args) {
f(std::forward<Args>(args)...);
repeater<N - 1>::do_work(f, args...);
}
};
template <>
struct repeater<0> {
template <typename F, typename ...Args>
static void do_work(F&&, Args&&...) {}
};
template <size_t N, typename F, typename ...Args2>
void repeat_for_n(F&& to_rep, Args2&&... args) {
repeater<N>::do_work(to_rep, args...);
}
template <typename T1, typename ...Args1, typename F, typename ...Args2>
void repeat_for_args(T1(Args1...), F&& to_rep, Args2&&... args) {
repeat_for_n<sizeof...(Args1)>(to_rep, args...);
}
And then you can use it like so:
repeat_for_args(some_function, [&]() {
(*func)(pop_t(outputstack,o_stackidx,o_stacksize))
});
In case you'd like to try it out yourself first:
Edit 1: To generate a function pointer type with N
repeating types in the parameter with the help of templates, you can do this:
Firstly, you need to define this helper class:
#include <tuple>
template <typename, typename>
struct make_sig_from_tuple;
template <typename R, typename ...Args>
struct make_sig_from_tuple<R, std::tuple<Args...>> {
using type = R(*)(Args...);
};
template <typename R, typename ...Args>
using make_sig_from_tuple_t = typename make_sig_from_tuple<R, Args...>::type;
And then we can do this,
template <typename T, size_t N>
struct generate_sig_impl {
using type = decltype(std::tuple_cat(std::declval<std::tuple<T>&>(), std::declval<typename generate_sig_impl<T, N - 1>::type&>()));
};
template <typename T>
struct generate_sig_impl<T, 0> {
using type = std::tuple<>;
};
template <typename R, typename T, size_t N>
struct generate_sig {
using type = make_sig_from_tuple_t<R, typename generate_sig_impl<T, N>::type>;
};
template <typename R, typename T, size_t N>
using generate_sig_t = typename generate_sig<R, T, N>::type;
Now it is possible to use it to cast variadic functions with just the value of N
without having to explicitly state the arguments in the type multiple times:
repeat_for_args(reinterpret_cast<generate_sig_t<float, float, 3>>(function_with_variadic_arguments), /* ... */);
// Equivalent to: repeat_for_args(reinterpret_cast<float(*)(float, float, float)>(function_with_variadic_arguments), /* ... */);
Edit 2: As the OP in the comments wants to reverse the order of the parameters as well, one can use this:
template <typename Arg1, typename ...Args>
struct reverse_func_sig_impl {
using type = decltype(std::tuple_cat(std::declval<typename reverse_func_sig_impl<Args...>::type&>(), std::declval<std::tuple<Arg1>&>()));
};
template <typename Arg1>
struct reverse_func_sig_impl<Arg1> {
using type = std::tuple<Arg1>;
};
template <typename>
struct reverse_func_sig;
template <typename R, typename... Args>
struct reverse_func_sig<R(Args...)> {
using type = make_sig_from_tuple_t<R, typename reverse_func_sig_impl<Args...>::type>;
};
template <typename FuncPtr>
using reverse_func_sig_t = typename reverse_func_sig<FuncPtr>::type;
Then use it like this:
repeat_for_args(reinterpret_cast<generate_sig_t<int(float, int)>>(function_with_variadic_arguments), /* ... */);
// Equivalent to: repeat_for_args(reinterpret_cast<int(int, float)>(function_with_variadic_arguments), /* ... */);