I'd like to replicate the following with BOOST FOREACH
std::vector<int>::const_iterator i1;
std::vector<int>::const_iterator i2;
for( i1 = v1.begin(), i2 = v2.begin();
i1 < v1.end() && i2 < v2.end();
++i1, ++i2 )
{
doSomething( *i1, *i2 );
}
Iterating over two things simultaneously is called a "zip" (from functional programming), and Boost has a zip iterator:
The zip iterator provides the ability to parallel-iterate over several
controlled sequences simultaneously. A zip iterator is constructed
from a tuple of iterators. Moving the zip iterator moves all the
iterators in parallel. Dereferencing the zip iterator returns a tuple
that contains the results of dereferencing the individual iterators.
Note that it's an iterator, not a range, so to use BOOST_FOREACH
you're going to have to stuff two of them into an iterator_range or pair
. So it won't be pretty, but with a bit of care you can probably come up with a simple zip_range
and write:
BOOST_FOREACH(boost::tuple<int,int> &p, zip_range(v1, v2)) {
doSomething(p.get<0>(), p.get<1>());
}
Or special-case for 2 and use std::pair
rather than boost::tuple
.
I suppose that since doSomething
might have parameters (int&, int&)
, actually we want a tuple<int&,int&>
. Hope it works.
If you use boost, I think it should be as simple as:
#include <boost/foreach.hpp>
#include <boost/range/combine.hpp>
std::vector<int> v1;
std::vector<int> v2;
// iterate over values
int i1, i2;
BOOST_FOREACH(boost::tie(i1, i2), boost::combine(v1, v2))
std::cout << i1+i2 << "\n"; // sums two vectors
// iterate over references
typedef boost::tuple<int&, int&> int_ref_tuple;
BOOST_FOREACH(int_ref_tuple tup, boost::combine(v1, v2))
tup.get<0>() = tup.get<1>(); // assigns one vector to another
the strange part is that boost::combine is not documented. Works for me, anyway.
If you want to use BOOST_FOREACH
to iterate two vectors simultenously, as you've done in your sample code, then you've to encapsulate both vectors in a wrapper class which should expose begin
and end
functions. These functions return custom iterator to be used to iterate over the wrapper which internally will iterate over the two vectors. Doesn't sound good, but that is what you've to do.
This is my first attempt to implement this (minimal implementation just to demonstrate the basic idea):
template<typename T>
struct wrapper
{
struct iterator
{
typedef typename std::vector<T>::iterator It;
It it1, it2;
iterator(It it1, It it2) : it1(it1), it2(it2) {}
iterator & operator++()
{
++it1; ++it2; return *this;
}
iterator & operator *()
{
return *this;
}
bool operator == (const iterator &other)
{
return !(*this != other);
}
bool operator != (const iterator &other)
{
return it1 != other.it1 && it2 != other.it2;
}
};
iterator begin_, end_;
wrapper(std::vector<T> &v1, std::vector<T> &v2)
: begin_(v1.begin(), v2.begin()),end_(v1.end(), v2.end())
{
}
wrapper(const wrapper & other) : begin_(other.begin_), end_(other.end_) {}
iterator begin()
{
return begin_;
}
iterator end()
{
return end_;
}
};
And the following is the test code. Since it's using usual for
loop, because ideone has not installed for boost for C++0x or I'm doing something wrong when including it.
int main() {
std::vector<int> v1 = {1,2,3,4,5,6};
std::vector<int> v2 = {11,12,13,14,15};
wrapper<int> w(v1,v2);
for(wrapper<int>::iterator it = w.begin(); it != w.end(); ++it)
{
std::cout << *it.it1 <<", "<< *it.it2 << std::endl;
}
return 0;
}
Output:
1, 11
2, 12
3, 13
4, 14
5, 15
Demo : http://ideone.com/Hf667
This is good for experimentation and learning purpose only, as I don't claim it to be perfect. There can be lots of improvement. And @Steve already has posted boost's solution.
Thanks to the answer of Steve Jessop and the great comments, I came up to the following solution, so if you find that nice, vote up Steve Jessop answer first. ;)
#include <iostream>
#include <vector>
#include <boost/typeof/typeof.hpp>
#include <boost/typeof/std/vector.hpp>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range/iterator_range.hpp>
using namespace boost;
int main(int argc, char **argv) {
std::vector<int> vecFirst = assign::list_of(1)(2)(3)(43)(7)(13);
std::vector<double> vecSecond = assign::list_of(53.45)(-23.545)(0.1574)(1.001)(0.0047)(9.7);
BOOST_AUTO(zipSequence,
make_iterator_range(
make_zip_iterator(make_tuple(vecFirst.begin(), vecSecond.begin())),
make_zip_iterator(make_tuple(vecFirst.end(), vecSecond.end()))
)
);
BOOST_FOREACH( BOOST_TYPEOF(*zipSequence.begin()) each, zipSequence) {
std::cout << "First vector value : " << each.get<0>()
<< " - Second vector value : " << each.get<1>()
<< std::endl;
}
}