So I have some type X
:
typedef ... X;
and a template function f
:
class <typename T> void f(X& x_out, const T& arg_in);
and then a function g
:
void g(const X* x_array, size_t x_array_size);
I need to write a variadic template function h
that does this:
template<typename... Args> void h(Args... args) { constexpr size_t nargs = sizeof...(args); // get number of args X x_array[nargs]; // create X array of that size for (int i = 0; i < nargs; i++) // foreach arg f(x_array[i], args[i]); // call f (doesn't work) g(x_array, nargs); // call g with x_array }
The reason it doesn't work is because you can't subscript args like that at runtime.
What is the best technique to replace the middle part of h
?
And the winner is Xeo:
template<class T> X fv(const T& t) { X x; f(x,t); return x; } template<class... Args> void h(Args... args) { X x_array[] = { fv(args)... }; g(x_array, sizeof...(Args)); }
(Actually in my specific case I can rewrite f to return x by value rather than as an out parameter, so I don't even need fv above)
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
A function parameter pack is a function parameter that accepts zero or more function arguments. A template with at least one parameter pack is called a variadic template.
Variadic functions are functions (e.g. printf) which take a variable number of arguments.
You could refactor or wrap f
to return a new X
instead of having it passed, since this would play pack expansion into the hand and make the function really concise:
template<class T> X fw(T const& t){ X x; f(x, t); return x; } template<class... Args> void h(Args... args){ X xs[] = { fw(args)... }; g(xs, sizeof...(Args)); }
Live example.
And if you could change g
to just accept an std::initializer_list
, it would get even more concise:
template<class... Args> void h(Args... args){ g({f(args)...}); }
Live example. Or (maybe better), you could also provide just a wrapper g
that forwards to the real g
:
void g(X const*, unsigned){} void g(std::initializer_list<X> const& xs){ g(xs.begin(), xs.size()); } template<class... Args> void h(Args... args){ g({f(args)...}); }
Live example.
Edit: Another option is using a temporary array:
template<class T> using Alias = T; template<class T> T& as_lvalue(T&& v){ return v; } template<class... Args> void h(Args... args){ g(as_lvalue(Alias<X[]>{f(args)...}), sizeof...(Args)); }
Live example. Note that the as_lvalue
function is dangerous, the array still only lives until the end of the full expression (in this case g
), so be cautious when using it. The Alias
is needed since just X[]{ ... }
is not allowed due to the language grammar.
If all of that's not possible, you'll need recursion to access all elements of the args
pack.
#include <tuple> template<unsigned> struct uint_{}; // compile-time integer for "iteration" template<unsigned N, class Tuple> void h_helper(X (&)[N], Tuple const&, uint_<N>){} template<unsigned N, class Tuple, unsigned I = 0> void h_helper(X (&xs)[N], Tuple const& args, uint_<I> = {}){ f(xs[I], std::get<I>(args)); h_helper(xs, args, uint_<I+1>()); } template<typename... Args> void h(Args... args) { static constexpr unsigned nargs = sizeof...(Args); X xs[nargs]; h_helper(xs, std::tie(args...)); g(xs, nargs); }
Live example.
Edit: Inspired by ecatmur's comment, I employed the indices trick to make it work with just pack expansion and with f
and g
as-is, without altering them.
template<unsigned... Indices> struct indices{ using next = indices<Indices..., sizeof...(Indices)>; }; template<unsigned N> struct build_indices{ using type = typename build_indices<N-1>::type::next; }; template <> struct build_indices<0>{ using type = indices<>; }; template<unsigned N> using IndicesFor = typename build_indices<N>::type; template<unsigned N, unsigned... Is, class... Args> void f_them_all(X (&xs)[N], indices<Is...>, Args... args){ int unused[] = {(f(xs[Is], args), 1)...}; (void)unused; } template<class... Args> void h(Args... args){ static constexpr unsigned nargs = sizeof...(Args); X xs[nargs]; f_them_all(xs, IndicesFor<nargs>(), args...); g(xs, nargs); }
Live example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With