/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2010-2018 Danny Robson */ #pragma once #include "iota.hpp" #include "../tuple/value.hpp" #include "../variadic.hpp" #include "../functor.hpp" namespace cruft::iterator { /////////////////////////////////////////////////////////////////////////// namespace detail::zip { /// A container that holds multiple iterators and returns a tuple of /// their results when dereferenced. /// /// \tparam IteratorT A tuple-like object that contains iterators template < typename IteratorT, typename = std::make_index_sequence> > struct zipped_iterator; template struct zipped_iterator> { public: // We can't declare ourselves as a forward_iterator because we're // unable to supply references to our value_type when we get // dereferenced unless we store the supplied values/references // inside ourself. // // This complicates implementation too much given we have no // pressing need for this functionality. using iterator_category = std::input_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = std::tuple< decltype( *std::get ( std::declval () ) )... >; using reference = value_type; using pointer = value_type*; zipped_iterator (IteratorT _iterators): m_iterators (_iterators) { ; } zipped_iterator& operator++ (void) { (++std::get (m_iterators), ...); return *this; } zipped_iterator operator++ (int); auto operator* (void) { // We deliberately construct a tuple manually here to reduce // the risk of dangling references. `forward_as_tuple` and // `make_tuple` have resulted in references to locals in the // past. return std::tuple< decltype(*std::get (m_iterators))... > ( *std::get (m_iterators)... ); } bool operator== (const zipped_iterator &rhs) const { return m_iterators == rhs.m_iterators; } bool operator!= (const zipped_iterator &rhs) const { return !(*this == rhs); } private: IteratorT m_iterators; }; /// A simple container for multiple collections that returns a /// wrapped multi-iterator for begin and end. /// /// It is up to the user to ensure StoreT does not contain dangling /// references. /// /// \tparam StoreT A tuple-like object of collections (or references /// to collections). template class collection { public: collection (StoreT&&... _store): m_store (std::forward (_store)...) { ; } using inner_t = std::tuple ()))...>; using indices_t = std::make_index_sequence; using iterator = zipped_iterator; iterator begin (void)& { return iterator ( tuple::value::map (::cruft::functor::begin {}, m_store) ); } iterator begin (void) const& { return iterator ( tuple::value::map (::cruft::functor::begin {}, m_store) ); } iterator end (void)& { return iterator ( tuple::value::map (cruft::functor::end {}, m_store) ); } iterator end (void) const & { return iterator ( tuple::value::map (cruft::functor::end {}, m_store) ); } /// Returns the number of elements in the sequence. decltype (auto) size (void) const { // All stores should have the same size so we arbitrarily pick // the first to query. return std::size (std::get<0> (m_store)); } private: std::tuple m_store; }; } ///------------------------------------------------------------------------ /// Takes a variable number of container arguments and returns an interable /// object with a value_type that is a tuple of the each container's /// value_type. /// /// The returned iterator value_type is suitable for using in range-for /// and structured bindings (and really, that's the entire point here). /// /// eg, cruft::zip ({1,2,3}, {4,5,6}) ~= {{1,4},{2,5},{3,6}} template decltype(auto) zip (ContainerT&&... data) { CHECK (((std::size (data) == std::size (variadic::get<0> (data...))) && ...)); return detail::zip::collection ( std::forward (data)... ); } ///------------------------------------------------------------------------ /// Takes a variable number of containers and returns a zipped iterable /// object where the first of the iterator's value_types is the index of /// that iterator. ie, It combines container offsets with value_types. /// /// The supplied containers _must_ not change size while the izip object /// is live. The size of the containers may be cached for efficiency. /// /// eg, cruft::izip ("abc") ~= {{0,'a'},{1,'b'},{2,'c'}} template decltype(auto) izip (ContainerT&&... data) { // Assume that all containers are the same size and create a range // that will supply integral indices over the first container. // // The call to `zip` will peform more rigorous tests on the sizes // of the container collection. return zip ( iota ( std::size (::cruft::variadic::get<0> (data...)) ), std::forward (data)... ); } }