Skip to content

Instantly share code, notes, and snippets.

@rubdos
Created March 30, 2016 20:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rubdos/c4e7f1302070f377cb978dec6472764f to your computer and use it in GitHub Desktop.
Save rubdos/c4e7f1302070f377cb978dec6472764f to your computer and use it in GitHub Desktop.
Simple iterator zip implementation.
/**
* Copyright 2016 Ruben De Smet
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <iterator>
#include <tuple>
#include <functional>
template<int ...>
struct seq { };
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
template <typename... IteratorType>
class zip_iterator
: public std::iterator<std::forward_iterator_tag,
std::tuple<decltype(std::bind(&IteratorType::operator*, nullptr)())...>>
{
private:
std::tuple<IteratorType...> _iterators;
template<std::size_t I>
inline typename std::enable_if< I == sizeof...(IteratorType), void>::type advance()
{
}
template<std::size_t I = 0>
inline typename std::enable_if< I < sizeof...(IteratorType), void>::type advance()
{
++(std::get<I>(_iterators));
advance<I+1>();
}
template<int... S>
std::tuple<decltype(std::bind(&IteratorType::operator*, nullptr)())...>
deref_impl(seq<S...>)
{
return std::make_tuple((*std::get<S>(_iterators))...);
}
template<std::size_t I>
inline typename std::enable_if< I == sizeof...(IteratorType), bool>::type
not_equals_impl(const zip_iterator& other)
{
return true;
}
template<std::size_t I = 0>
inline typename std::enable_if< I < sizeof...(IteratorType), bool>::type
not_equals_impl(const zip_iterator& other)
{
if(std::get<I>(_iterators) == std::get<I>(other._iterators))
return false;
return not_equals_impl<I+1>(other);
}
public:
zip_iterator(const std::tuple<IteratorType...>& iterators)
: _iterators(iterators)
{
}
std::tuple<decltype(std::bind(&IteratorType::operator*, nullptr)())...>
operator*()
{
return deref_impl(typename gens<sizeof...(IteratorType)>::type());
}
const zip_iterator& operator++()
{
this->advance();
return *this;
}
bool operator!=(const zip_iterator& other)
{
return not_equals_impl(other);
}
};
template<typename T>
class iterator_range
{
T _beg;
T _end;
public:
iterator_range(T& beg, T& end)
: _beg(beg), _end(end)
{
}
T begin()
{
return _beg;
}
T end()
{
return _end;
}
};
template<typename IteratorType>
iterator_range<IteratorType> make_iterator_range(IteratorType begin, IteratorType end)
{
return iterator_range<IteratorType>(begin, end);
}
template<typename... IterableType>
static auto zip(const IterableType&... containers)
-> iterator_range<zip_iterator<decltype(std::begin(containers))...>>
{
zip_iterator<decltype(std::begin(containers))...> begin(std::make_tuple(std::begin(containers)...));
zip_iterator<decltype(std::begin(containers))...> end(std::make_tuple(std::end(containers)...));
return make_iterator_range(begin, end);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment