Monday, July 19, 2010

Unpacking tuples for function arguments

Sometimes you have a tuple and want to pass its contents to a function as arguments. One way is to overload the function:

int average(int a, int b, int c)
{
  return (a + b + c) / 3;
}

int average(std::tuple<int,int,int> x)
{
  return average(std::get<0>(x), std::get<1>(x), std::get<2>(x));
}

This works for an individual function or two, but it isn't a general solution and it doesn't scale. So let's automate it.

#include <functional> 
#include <tuple> 

namespace detail { 

template<std::size_t... Indices>
struct indices { typedef indices<Indices...,sizeof...(Indices)> next; }; 

// Generates the type indices<0,1,...,N-1> which is used to unpack an N-tuple.
template<std::size_t N>
struct make_indices { typedef typename make_indices<N-1>::type::next type; };

template<>
struct make_indices<0> { typedef indices<> type; };

// We use the indices to unpack the tuple.
template<class Function, class... T, std::size_t... Indices> 
inline typename std::result_of<Function(T...)>::type 
apply(Function f, std::tuple<T...>&& x, indices<Indices...>) 
{ 
  return f(std::forward<T>(std::get<Indices>(x))...); 
} 

} // namespace detail 

// Apply function f to the contents of tuple x.
template<class Function, class... T> 
inline typename std::result_of<Function(T...)>::type 
apply(Function f, std::tuple<T...>&& x) 
{ 
  typedef typename detail::make_indices<sizeof...(T)>::type Indices; 
  return detail::apply(f, std::forward<std::tuple<T...>>(x), Indices()); 
}

Now we can call our functions this way:

int average(int a, int b, int c) 
{ 
  return (a + b + c) / 3; 
} 

int zero() 
{ 
  return 0; 
} 

int main() 
{ 
  std::cout << apply(average, std::make_tuple(1, 2, 3)) << '\n'; 

  std::cout << apply(zero, std::make_tuple()) << '\n'; 

  std::cout << apply([](int x, int y){ return x + y; }, std::make_tuple(2, 3)) << '\n'; 
}

No comments:

Post a Comment