Boost logo

Boost :

Subject: [boost] Boost range - Add variadic join/zip
From: Gonzalo BG (gonzalobg88_at_[hidden])
Date: 2013-05-09 11:01:57


Code available here: https://gist.github.com/gnzlbg/5547905

Let,

std::array<double,4> a = {{ 1, 2, 3, 4 }};
std::list<int> b = { 11, 22, 33, 44 };
std::deque<int> c = { 111, 222, 333, 444 };
std::vector<int> d = {1111,2222,3333,4444 };

If the values stored in a container have the same type, I would like to
iterate over different containers as if it were a single one, and:

/// - modify its value
for(auto&& i : join(b,c,d)) { i += 1; }

/// - use it with boost algorithms
boost::transform(join(b,c,d),begin(join(b,c,d)),[&](int j){ return j * 2;
});

/// - and read it
for(const auto& i : join(b,c,d)) { std::cout << i << "\n"; }

Another problem I usually face is manipulating data that has different
types. This data is usually stored in different containers for efficiency,
but it does belong together. Zip can be used to iterate throw a row of this
kind of table-like data structure:

for(const auto& t : zip(a,b,c,d)) { std::cout << t << "\n"; }

However, boost's zip_iterators are not writable. I don't really think it
would be possible to make them writable, but that would allow code like
this:

typedef decltype(*begin(zip(a,c,d))) tIt;
boost::sort(zip(a, c, d), [](const tIt& i, const tIt& j){
                          return boost::get<0>(i) > boost::get<0>(j); });

which sorts the three containers in lock-step after the values in the first
container (a). Some parts of this code can be implemented with minor
extensions to boost range. A variadic join might look like this:

template<class C>
auto join(C&& c) -> decltype(boost::make_iterator_range(c)) {
  return boost::make_iterator_range(c);
}

template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
->
decltype(boost::join(boost::join(boost::make_iterator_range(std::forward<C>(c)),

boost::make_iterator_range(std::forward<D>(d))),
                        join(std::forward<Args>(args)...))) {
  return
boost::join(boost::join(boost::make_iterator_range(std::forward<C>(c)),

 boost::make_iterator_range(std::forward<D>(d))),
                     join(std::forward<Args>(args)...));
}

A variadic zip is more complicated. If we want write access we cannot use
boost zip_iterator. Still one can use Anthony Williams' TupleIterator:

template <class... T>
auto zip(T&&... c)
-> boost::iterator_range<

decltype(iterators::makeTupleIterator(std::begin(std::forward<T>(c))...))> {
  return boost::make_iterator_range
      (iterators::makeTupleIterator(std::begin(std::forward<T>(c))...),
       iterators::makeTupleIterator(std::end(std::forward<T>(c))...));
}

For read-only access one could use boost::zip_iterator, but I think
write-access is _really_ important (e.g. sort wouldn't work).

Would it be possible to add similar functionality to boost range?

This code wouldn't work without the help of everyone who participated in
the following SO discussions:
-
http://stackoverflow.com/questions/14366576/boostrangejoin-for-multiple-ranges
-
http://stackoverflow.com/questions/13840998/sorting-zipped-locked-containers-in-c-using-boost-or-the-stl
and without Anthony Williams's tupleIterator. See also
http://www.justsoftwaresolutions.co.uk/articles/pair_iterators.pdf

Bests,
Gonzalo BG


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk