C ++ zip variation templates

Here is a simple two-loop zip function in C ++:

template <typename A, typename B> std::list<std::pair<A, B> > simple_zip(const std::list<A> & lhs, const std::list<B> & rhs) { std::list<std::pair<A, B> > result; for (std::pair<typename std::list<A>::const_iterator, typename std::list<B>::const_iterator> iter = std::pair<typename std::list<A>::const_iterator, typename std::list<B>::const_iterator>(lhs.cbegin(), rhs.cbegin()); iter.first != lhs.end() && iter.second != rhs.end(); ++iter.first, ++iter.second) { result.push_back( std::pair<A, B>(*iter.first, *iter.second) ); } return result; } 

How can I extend this to an arbitrary number of containers with variable templates?

I would like general_zip to accept a tuple of list (each list may contain a different type) and return a list from tuple s.

+8
c ++ iterator c ++ 11 tuples variadic-templates
source share
3 answers

It seems like this should work

 std::list<std::tuple<>> simple_zip() { return {}; } template <typename ...T> std::list<std::tuple<T...>> simple_zip(std::list<T>... lst) { std::list<std::tuple<T...>> result; for (int i = 0, e = std::min({lst.size()...}); i != e; i++) { result.emplace_back(std::move(lst.front())...); [](...){} ((lst.pop_front(), 0)...); } return result; } 

@Potatoswatter had a good note (IMO) that could copy more than needed when the lists are of different sizes and that using iterators only would be better as pop_front does more than necessary. I think the next one fixes the iterator one at the expense of more code.

 template <typename ...T> std::list<std::tuple<T...>> simple_zip(std::list<T>... lst) { std::list<std::tuple<T...>> result; struct { void operator()(std::list<std::tuple<T...>> &t, int c, typename std::list<T>::iterator ...it) { if(c == 0) return; t.emplace_back(std::move(*it++)...); (*this)(t, c-1, it...); } } zip; zip(result, std::min({lst.size()...}), lst.begin()...); return result; } std::list<std::tuple<>> simple_zip() { return {}; } 
+13
source share

This is a gradual improvement in Johannes' first answer. It avoids the struct dummy and avoids copying all input lists (although this does not make much difference if only one list is shorter than the others). I also made it common to all containers.

But this requires a package template index generator, which is quite useful anyway

 template< std::size_t n, typename ... acc > struct make_index_tuple { typedef typename make_index_tuple< n - 1, std::integral_constant< std::size_t, n - 1 >, acc ... >::type type; }; template< typename ... acc > struct make_index_tuple< 0, acc ... > { typedef std::tuple< acc ... > type; }; 

A β€œreal” implementation consists of a simple function that requires output from the aforementioned utility and an interface function that maps packets to tuples.

 template< typename ret_t, std::size_t ... indexes, typename lst_tuple > ret_t simple_zip( std::tuple< std::integral_constant< std::size_t, indexes > ... >, lst_tuple const &lst ) { ret_t ret; auto iters = std::make_tuple( std::get< indexes >( lst ).begin() ... ); auto ends = std::make_tuple( std::get< indexes >( lst ).end() ... ); while ( std::max< bool >({ std::get< indexes >( iters ) == std::get< indexes >( ends ) ... }) == false ) { ret.emplace_back( * std::get< indexes >( iters ) ++ ... ); } return ret; } template< typename ... T > std::list< std::tuple< typename T::value_type ... > > simple_zip( T const & ... lst ) { return simple_zip < std::list< std::tuple< typename T::value_type ... > > > ( typename make_index_tuple< sizeof ... lst >::type(), std::tie( lst ... ) ); } 

At least that puts in perspective what Johannes did. This is how most algorithms with a variational pattern look like, because there is no way to save a variable state of a type without tuple , and there is no way to process a variational tuple without a package of indices or a metarecursive function. (Edit: ah, now Johannes used a recursive local tail function to define local packages to do all this without tuple at all. Amazing ... if you can handle all the functional programming; v).)

+4
source share

Another version: Johannes and Potatoswatter's answer, trying to minimize the amount of cheating (which I nonetheless enjoyed!)

 template <typename C, typename... Its> void simple_zip_details(C& c, size_t size, Its... its) { for (int i = 0; i < size; i++) c.emplace_back(std::move(*its++)...); } template <typename... Ts> std::list<std::tuple<Ts...>> simple_zip(std::list<Ts>... lst) { std::list<std::tuple<Ts...>> result; size_t size = std::min({ lst.size()... }); simple_zip_details(result, size, lst.begin()...); return result; } 
+1
source share

All Articles