Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a function for each variadic template argument and an array

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)

like image 479
Andrew Tomazos Avatar asked Aug 19 '12 22:08

Andrew Tomazos


People also ask

What is Variadic template in C++?

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.

What is parameter pack in c++?

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.

Is printf Variadic function?

Variadic functions are functions (e.g. printf) which take a variable number of arguments.


1 Answers

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.

like image 62
Xeo Avatar answered Sep 24 '22 07:09

Xeo