Perhaps not the most useful, but there's a duplicate of this question that seems to have a solution from Anthony Williams.
Sorting zipped (locked) containers in C++ using boost or the STL
Here's the full example which seems to work (note I just put it together... I didn't write this.):
#include <boost/range.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <cstddef>
#include <algorithm>
#include <stdexcept>
#include <new>
#include "boost/tuple/tuple.hpp"
#include "boost/tuple/tuple_comparison.hpp"
#include "boost/utility.hpp"
#include "boost/type_traits.hpp"
#include "boost/optional.hpp" // for aligned_storage
#include <memory>
namespace iterators
{
namespace detail
{
void preincrementTuple(boost::tuples::null_type)
{
}
template<typename TupleType>
void preincrementTuple(TupleType& lhs)
{
preincrementTuple(lhs.get_tail());
++(lhs.template get<0>());
}
void predecrementTuple(boost::tuples::null_type)
{
}
template<typename TupleType>
void predecrementTuple(TupleType& lhs)
{
predecrementTuple(lhs.get_tail());
--(lhs.template get<0>());
}
template<typename difference_type>
void addToTuple(boost::tuples::null_type, difference_type)
{
}
template<typename difference_type, typename TupleType>
void addToTuple(TupleType& lhs, difference_type diff)
{
addToTuple(lhs.get_tail(), diff);
lhs.template get<0>() += diff;
}
template<typename difference_type>
void subFromTuple(boost::tuples::null_type, difference_type)
{
}
template<typename difference_type, typename TupleType>
void subFromTuple(TupleType& lhs, difference_type diff)
{
subFromTuple(lhs.get_tail(), diff);
lhs.template get<0>() -= diff;
}
template<typename difference_type, typename TupleType>
difference_type diffTuples(TupleType const& lhs, TupleType const& rhs);
template<typename difference_type, typename TupleType>
struct DiffTupleHelper
{
static difference_type doDiff(TupleType const& lhs, TupleType const& rhs)
{
difference_type res1 = lhs.template get<0>() - rhs.template get<0>();
difference_type res2 = diffTuples<difference_type>(lhs.get_tail(), rhs.get_tail());
if (res1 == res2)
{
return res1;
}
throw std::logic_error("The iterators in the tuples are mismatched");
}
};
template<typename difference_type, typename ValueType>
struct DiffTupleHelper<difference_type, boost::tuples::cons<ValueType, boost::tuples::null_type> >
{
static difference_type doDiff(boost::tuples::cons<ValueType, boost::tuples::null_type> const& lhs, boost::tuples::cons<ValueType, boost::tuples::null_type> const& rhs)
{
return lhs.template get<0>() - rhs.template get<0>();
}
};
template<typename difference_type, typename TupleType>
difference_type diffTuples(TupleType const& lhs, TupleType const& rhs)
{
return DiffTupleHelper<difference_type, TupleType>::doDiff(lhs, rhs);
}
template<typename SourceTuple>
struct MakeTupleTypeWithReferences
{
typedef MakeTupleTypeWithReferences<typename SourceTuple::tail_type> TailTupleTypeBuilder;
typedef typename TailTupleTypeBuilder::Type TailTupleType;
typedef boost::tuples::cons<typename boost::add_reference<typename SourceTuple::head_type>::type,
TailTupleType> Type;
template<typename Tuple>
static Type makeTuple(Tuple& source)
{
return Type(source.get_head(), TailTupleTypeBuilder::makeTuple(source.get_tail()));
}
};
template<>
struct MakeTupleTypeWithReferences<boost::tuples::null_type>
{
typedef boost::tuples::null_type Type;
static Type makeTuple(boost::tuples::null_type)
{
return Type();
}
};
typedef char Tiny;
struct Small
{
Tiny dummy[2];
};
struct Medium
{
Small dummy[2];
};
struct Large
{
Medium dummy[2];
};
struct Huge
{
Large dummy[2];
};
template<unsigned>
struct CategoryMap
{
typedef void Type;
};
// Tiny categoryCheck(std::output_iterator_tag*);
Small categoryCheck(std::input_iterator_tag*);
Medium categoryCheck(std::forward_iterator_tag*);
Large categoryCheck(std::bidirectional_iterator_tag*);
Huge categoryCheck(std::random_access_iterator_tag*);
// template<>
// struct CategoryMap<sizeof(Tiny)>
// {
// typedef std::output_iterator_tag Type;
// };
template<>
struct CategoryMap<sizeof(Small)>
{
typedef std::input_iterator_tag Type;
};
template<>
struct CategoryMap<sizeof(Medium)>
{
typedef std::forward_iterator_tag Type;
};
template<>
struct CategoryMap<sizeof(Large)>
{
typedef std::bidirectional_iterator_tag Type;
};
template<>
struct CategoryMap<sizeof(Huge)>
{
typedef std::random_access_iterator_tag Type;
};
template<typename Cat1, typename Cat2>
struct CommonCategory
{
private:
enum
{
categorySize = sizeof(::iterators::detail::categoryCheck(false ? (Cat1*)0 : (Cat2*)0))
};
public:
typedef typename CategoryMap<categorySize>::Type Type;
};
// specializations
template<typename Cat>
struct CommonCategory<std::output_iterator_tag, Cat>
{
typedef std::output_iterator_tag Type;
};
template<typename Cat>
struct CommonCategory<Cat, std::output_iterator_tag>
{
typedef std::output_iterator_tag Type;
};
template<>
struct CommonCategory<std::output_iterator_tag, std::output_iterator_tag>
{
typedef std::output_iterator_tag Type;
};
template<>
struct CommonCategory<std::input_iterator_tag, std::output_iterator_tag>
{
// no Type, because error
};
template<>
struct CommonCategory<std::output_iterator_tag, std::input_iterator_tag>
{
// no Type, because error
};
void derefAndWrite(boost::tuples::null_type, boost::tuples::null_type)
{}
template<typename IterTuple, typename SourceTuple>
void derefAndWrite(IterTuple& iters, SourceTuple const& source)
{
*iters.get_head() = source.get_head();
derefAndWrite(iters.get_tail(), source.get_tail());
}
}
// An OutputTuple holds a tuple of references to iterators, and writes to them on assignment
template<typename IterTuple>
struct OutputTuple :
public detail::MakeTupleTypeWithReferences<IterTuple>::Type,
boost::noncopyable
{
private:
typedef detail::MakeTupleTypeWithReferences<IterTuple> BaseTypeBuilder;
typedef typename BaseTypeBuilder::Type BaseType;
public:
OutputTuple(IterTuple& iters) :
BaseType(BaseTypeBuilder::makeTuple(iters))
{}
template<typename SomeTuple>
OutputTuple& operator=(const SomeTuple& other)
{
detail::derefAndWrite(static_cast<BaseType&>(*this), other);
return *this;
}
};
// An OwningRefTuple holds a tuple of references,
// which may point to data within the tuple, or external to it
namespace detail
{
struct PreserveReferences
{};
template<typename OwnedType>
struct OwningBase
{
std::auto_ptr<OwnedType> tupleBuf;
OwningBase()
{}
template<typename SomeType>
OwningBase(SomeType &source) :
tupleBuf(new OwnedType(source))
{}
};
}
template<typename TupleType>
struct OwningRefTuple :
private detail::OwningBase<TupleType>,
public detail::MakeTupleTypeWithReferences<TupleType>::Type
{
private:
typedef detail::MakeTupleTypeWithReferences<TupleType> BaseTypeBuilder;
typedef typename BaseTypeBuilder::Type BaseType;
typedef detail::OwningBase<TupleType> OwningBaseType;
public:
typedef typename BaseType::head_type head_type;
typedef typename BaseType::tail_type tail_type;
private:
typedef TupleType OwnedTuple;
OwnedTuple* getTuplePtr()
{
return this->tupleBuf.get();
}
public:
// copy from other types of tuples too
template<typename SomeTuple>
OwningRefTuple(const SomeTuple& other) :
OwningBaseType(other), BaseType(BaseTypeBuilder::makeTuple(*getTuplePtr()))
{
}
// copying copies values by default
OwningRefTuple(const OwningRefTuple& other) :
OwningBaseType(other), BaseType(BaseTypeBuilder::makeTuple(*getTuplePtr()))
{
}
// allow user to specify
// whether to preserve references
template<typename SomeTuple>
OwningRefTuple(SomeTuple& other, detail::PreserveReferences const&) :
BaseType(BaseTypeBuilder::makeTuple(other))
{
}
// assignment assigns to referenced values
template<typename SomeTuple>
OwningRefTuple& operator=(const SomeTuple& other)
{
BaseType::operator=(other);
return *this;
}
OwningRefTuple& operator=(const OwningRefTuple& other)
{
BaseType::operator=(other);
return *this;
}
};
namespace detail
{
template<typename IterTuple>
struct DerefIterTupleHelperKeepRef
{
typedef boost::tuples::cons<typename boost::add_reference<typename std::iterator_traits<typename IterTuple::head_type>::value_type>::type,
typename DerefIterTupleHelperKeepRef<typename IterTuple::tail_type>::Type> Type;
};
template<>
struct DerefIterTupleHelperKeepRef<boost::tuples::null_type>
{
typedef boost::tuples::null_type Type;
};
template<>
struct DerefIterTupleHelperKeepRef<const boost::tuples::null_type>
{
typedef boost::tuples::null_type Type;
};
template<typename IterTuple>
struct DerefIterTupleHelperNoRef
{
typedef boost::tuples::cons<typename std::iterator_traits<typename IterTuple::head_type>::value_type,
typename DerefIterTupleHelperNoRef<typename IterTuple::tail_type>::Type> Type;
};
template<>
struct DerefIterTupleHelperNoRef<boost::tuples::null_type>
{
typedef boost::tuples::null_type Type;
};
template<>
struct DerefIterTupleHelperNoRef<const boost::tuples::null_type>
{
typedef boost::tuples::null_type Type;
};
boost::tuples::null_type derefIterTupleKeepRef(boost::tuples::null_type const& iters)
{
return iters;
}
template<typename IterTuple>
const typename DerefIterTupleHelperKeepRef<IterTuple>::Type derefIterTupleKeepRef(IterTuple& iters)
{
return typename DerefIterTupleHelperKeepRef<IterTuple>::Type(*iters.template get<0>(), derefIterTupleKeepRef(iters.get_tail()));
}
boost::tuples::null_type derefIterTupleNoRef(boost::tuples::null_type const& iters)
{
return iters;
}
template<typename IterTuple>
typename DerefIterTupleHelperNoRef<IterTuple>::Type derefIterTupleNoRef(IterTuple& iters)
{
return typename DerefIterTupleHelperNoRef<IterTuple>::Type(*iters.template get<0>(), derefIterTupleNoRef(iters.get_tail()));
}
// Define, construct and destroy the appropriate value_type for
// the given iterator category
template<typename Category, typename IterTuple>
struct ValueForCategory
{
private:
typedef typename IterTuple::head_type HeadIterType;
typedef typename IterTuple::tail_type TailTupleType;
typedef typename std::iterator_traits<HeadIterType>::value_type HeadValueType;
typedef typename ValueForCategory<Category, TailTupleType>::ValueTuple TailValueTuple;
public:
typedef boost::tuples::cons<HeadValueType, TailValueTuple> ValueTuple;
typedef OwningRefTuple<ValueTuple> value_type;
typedef value_type Type;
static void construct(Type* p, IterTuple const& iters)
{
// don't copy values, keep as references
new (p)Type(derefIterTupleKeepRef(iters), ::iterators::detail::PreserveReferences());
}
static void destruct(Type* p)
{
p->~OwningRefTuple<ValueTuple>();
}
};
template<typename Category>
struct ValueForCategory<Category, boost::tuples::null_type>
{
private:
public:
typedef boost::tuples::null_type ValueTuple;
};
template<typename IterTuple>
struct ValueForCategory<std::input_iterator_tag, IterTuple>
{
private:
typedef typename IterTuple::head_type HeadIterType;
typedef typename IterTuple::tail_type TailTupleType;
typedef typename std::iterator_traits<HeadIterType>::value_type HeadValueType;
typedef typename ValueForCategory<std::input_iterator_tag, TailTupleType>::ValueTuple TailValueTuple;
public:
typedef boost::tuples::cons<HeadValueType, TailValueTuple> ValueTuple;
typedef OwningRefTuple<ValueTuple> value_type;
typedef value_type Type;
static void construct(Type* p, IterTuple const& iters)
{
// copy values
new (p)Type(derefIterTupleNoRef(iters));
}
static void destruct(Type* p)
{
p->~OwningRefTuple<ValueTuple>();
}
};
template<>
struct ValueForCategory<std::input_iterator_tag, boost::tuples::null_type>
{
private:
public:
typedef boost::tuples::null_type ValueTuple;
};
template<typename IterTuple>
struct ValueForCategory<std::output_iterator_tag, IterTuple>
{
public:
typedef OutputTuple<IterTuple> value_type;
typedef value_type Type;
static void construct(Type* p, IterTuple& iters)
{
// copy values
new (p)Type(iters);
}
static void destruct(Type* p)
{
p->~OutputTuple<IterTuple>();
}
};
template<>
struct ValueForCategory<std::output_iterator_tag, boost::tuples::null_type>
{
private:
public:
};
template<typename Category, typename IterTuple>
struct VFCSelector
{
typedef ValueForCategory<Category, IterTuple> Type;
};
// Select the iterator_category and value_type for our TupleIt
template<typename IterTuple>
struct TupleItHelper
{
typedef typename IterTuple::head_type HeadIterType;
typedef typename IterTuple::tail_type TailTupleType;
typedef typename std::iterator_traits<HeadIterType>::iterator_category Cat1;
typedef typename TupleItHelper<TailTupleType>::iterator_category Cat2;
typedef typename CommonCategory<Cat1, Cat2>::Type iterator_category;
typedef typename VFCSelector<iterator_category, IterTuple>::Type ValueTypeDef;
typedef typename ValueTypeDef::value_type value_type;
typedef typename ValueTypeDef::Type DeRefType;
typedef DeRefType& reference;
typedef DeRefType* pointer;
typedef std::ptrdiff_t difference_type;
typedef std::iterator<iterator_category, value_type, difference_type, pointer, reference> IteratorType;
static void construct(DeRefType* p, IterTuple& iters)
{
ValueTypeDef::construct(p, iters);
}
static void destruct(DeRefType* p)
{
ValueTypeDef::destruct(p);
}
};
template<>
struct TupleItHelper<boost::tuples::null_type>
{
typedef std::random_access_iterator_tag iterator_category;
};
}
// the actual Tuple Iterator itself
template<typename IterTuple>
struct TupleIt :
public detail::TupleItHelper<IterTuple>::IteratorType
{
private:
typedef detail::TupleItHelper<IterTuple> TupleDefs;
public:
typedef typename TupleDefs::iterator_category iterator_category;
typedef typename TupleDefs::value_type value_type;
typedef typename TupleDefs::difference_type difference_type;
typedef typename TupleDefs::reference reference;
typedef typename TupleDefs::pointer pointer;
private:
pointer getValuePtr() const
{
return reinterpret_cast<pointer>(dataCache.address());
}
void emptyCache() const
{
if (cacheInitialized)
{
TupleDefs::destruct(getValuePtr());
cacheInitialized = false;
}
}
void initCache() const
{
emptyCache();
TupleDefs::construct(getValuePtr(), iters);
cacheInitialized = true;
}
public:
TupleIt(IterTuple iters_) :
iters(iters_), cacheInitialized(false)
{}
template<typename OtherIterTuple>
TupleIt(const TupleIt<OtherIterTuple>& other) :
iters(other.iters), cacheInitialized(false)
{}
TupleIt(const TupleIt& other) :
iters(other.iters), cacheInitialized(false)
{}
TupleIt() :
iters(), cacheInitialized(false)
{}
~TupleIt()
{
emptyCache();
}
void swap(TupleIt& other)
{
using std::swap;
swap(iters, other.iters);
}
TupleIt& operator=(TupleIt const& other)
{
emptyCache();
iters = other.iters;
return *this;
}
// Input Iterator requirements
reference operator*() const
{
initCache();
return *getValuePtr();
}
pointer operator->() const
{
initCache();
return getValuePtr();
}
friend bool operator==(const TupleIt& lhs, const TupleIt& rhs)
{
return lhs.iters == rhs.iters;
}
friend bool operator!=(const TupleIt& lhs, const TupleIt& rhs)
{
return lhs.iters != rhs.iters;
}
// Forward Iterator requirements
TupleIt& operator++()
{
detail::preincrementTuple(iters);
return *this;
}
TupleIt operator++(int)
{
TupleIt temp(*this);
++*this;
return temp;
}
// Bidirectional Iterator requirements
TupleIt& operator--()
{
detail::predecrementTuple(iters);
return *this;
}
TupleIt operator--(int)
{
TupleIt temp(*this);
--*this;
return temp;
}
// Random-Access Iterator requirements
TupleIt& operator+=(difference_type n)
{
detail::addToTuple(iters, n);
return *this;
}
TupleIt& operator-=(difference_type n)
{
detail::subFromTuple(iters, n);
return *this;
}
friend difference_type operator-(const TupleIt& a, const TupleIt& b)
{
return detail::diffTuples<difference_type>(a.iters, b.iters);
}
value_type operator[](difference_type n) const
{
return *(*this + n);
}
private:
// everything is mutable so we can modify it without affecting const correctness
// of client code
mutable IterTuple iters;
mutable boost::optional_detail::aligned_storage<typename TupleDefs::DeRefType> dataCache;
mutable bool cacheInitialized;
};
// more random-access iterator requirements
template<typename IterTuple>
TupleIt<IterTuple> operator+(std::ptrdiff_t n, TupleIt<IterTuple> temp)
{
temp += n;
return temp;
}
template<typename IterTuple>
TupleIt<IterTuple> operator+(TupleIt<IterTuple> temp, std::ptrdiff_t n)
{
temp += n;
return temp;
}
template<typename IterTuple>
TupleIt<IterTuple> operator-(TupleIt<IterTuple> temp, std::ptrdiff_t n)
{
temp -= n;
return temp;
}
template<typename IterTuple, typename IterTuple2>
bool operator<(const TupleIt<IterTuple>& a, const TupleIt<IterTuple2>& b)
{
return (b - a)>0;
}
template<typename IterTuple, typename IterTuple2>
bool operator>(const TupleIt<IterTuple>& a, const TupleIt<IterTuple2>& b)
{
return b < a;
}
template<typename IterTuple, typename IterTuple2>
bool operator>=(const TupleIt<IterTuple>& a, const TupleIt<IterTuple2>& b)
{
return !(b<a);
}
template<typename IterTuple, typename IterTuple2>
bool operator<=(const TupleIt<IterTuple>& a, const TupleIt<IterTuple2>& b)
{
return !(b>a);
}
// implementation of swap and iter_swap
template<typename IterTuple>
void swap(TupleIt<IterTuple>& lhs, TupleIt<IterTuple>& rhs)
{
lhs.swap(rhs);
}
// template<typename IterTuple,IterTuple2>
// void iter_swap(const TupleIt<IterTuple>& lhs,const TupleIt<IterTuple2>& rhs)
// {
// lhs.iter_swap(rhs);
// }
template<typename Iter1, typename Iter2>
TupleIt<typename boost::tuples::tuple<Iter1, Iter2> > makeTupleIterator(Iter1 i1, Iter2 i2)
{
return TupleIt<typename boost::tuples::tuple<Iter1, Iter2> >(boost::make_tuple(i1, i2));
}
template<typename Iter1, typename Iter2, typename Iter3>
TupleIt<typename boost::tuples::tuple<Iter1, Iter2, Iter3> > makeTupleIterator(Iter1 i1, Iter2 i2, Iter3 i3)
{
return TupleIt<typename boost::tuples::tuple<Iter1, Iter2, Iter3> >(boost::make_tuple(i1, i2, i3));
}
template<typename Iter1, typename Iter2, typename Iter3, typename Iter4>
TupleIt<typename boost::tuples::tuple<Iter1, Iter2, Iter3, Iter4> > makeTupleIterator(Iter1 i1, Iter2 i2, Iter3 i3, Iter4 i4)
{
return TupleIt<typename boost::tuples::tuple<Iter1, Iter2, Iter3, Iter4> >(boost::make_tuple(i1, i2, i3, i4));
}
}
template <typename... T>
auto zip(T&... containers) -> boost::iterator_range<decltype(iterators::makeTupleIterator(std::begin(containers)...))>
{
return boost::make_iterator_range(iterators::makeTupleIterator(std::begin(containers)...), iterators::makeTupleIterator(std::end(containers)...));
}
int main()
{
std::vector<std::string> firstname = { "Bjarne", "Alexander", "Dennis", "James", "James" };
std::vector<std::string> lastname = { "Stroustrup", "Stepanov", "Ritchie", "Coplien", "Gosling" };
auto zipped = zip(firstname, lastname);
for (auto it = boost::begin(zipped), end = boost::end(zipped); it != end; ++it)
std::cout << *it << std::endl;
std::cout << "----------" << std::endl;
std::sort(boost::begin(zipped), boost::end(zipped));
for (auto it = boost::begin(zipped), end = boost::end(zipped); it != end; ++it)
std::cout << *it << std::endl;
return 0;
}