diff --git a/iterator.hpp b/iterator.hpp index 3c2e2bef..e9770bec 100644 --- a/iterator.hpp +++ b/iterator.hpp @@ -216,25 +216,31 @@ namespace util { /////////////////////////////////////////////////////////////////////////// namespace detail::zip { + // holds a tuple of iterators for begin and end, and returns an + // iterator that transforms these iterators into tuples of value_types. + // + // this must be expressed in terms of iterators, rather than containers, + // because it dramatically simplifies iterating over raw arrays. template < - typename ContainerT, typename IteratorT, - typename I = std::make_index_sequence::value> + typename I = std::make_index_sequence::value> > - struct collection; + class collection; + + //--------------------------------------------------------------------- template < - typename ContainerT, typename IteratorT, std::size_t ...I > - struct collection< - ContainerT, + class collection< IteratorT, std::index_sequence > { - collection (const ContainerT &_containers): - m_containers { _containers } + public: + collection (const IteratorT &_begin, const IteratorT &_end): + m_begin { _begin }, + m_end { _end } { ; } struct iterator : std::iterator< @@ -246,13 +252,14 @@ namespace util { >, std::size_t > { - IteratorT m_iterators; - - iterator (IteratorT _iterators): + public: + iterator (const IteratorT &_iterators): m_iterators (_iterators) { ; } - iterator& operator++ (void) + + iterator& + operator++ (void) { // HACK: we don't actually need to create a tuple here, // but it's a zero cost method to expand the parameter @@ -261,58 +268,86 @@ namespace util { return *this; } + iterator operator++ (int); - auto operator* (void) + + auto + operator* (void) { return std::make_tuple (*std::get (m_iterators)...); } - bool operator== (const iterator &rhs) const + + bool + operator== (const iterator &rhs) const { return m_iterators == rhs.m_iterators; } - bool operator!= (const iterator &rhs) const + + bool + operator!= (const iterator &rhs) const { return !(*this == rhs); } + + private: + IteratorT m_iterators; }; - iterator begin (void) + + iterator + begin (void) { - return iterator { { std::begin (std::get (m_containers))... } }; + return iterator { { std::get (m_begin)... } }; } - iterator end (void) + + iterator + end (void) { - return iterator { { std::end (std::get (m_containers))... } }; + return iterator { { std::get (m_end)... } }; } - ContainerT m_containers; + + private: + IteratorT m_begin; + IteratorT m_end; }; } - //------------------------------------------------------------------------- + ///------------------------------------------------------------------------ + /// takes a variable number of container arguments and returns an interable + /// object with a value_type of tuple of the argument's value_types. + /// + /// the returned iterator value_type is suitable for using in range-for + /// and structured bindings (and really, that's the entire point here). + /// + /// eg, util::zip ({1,2,3}, {4,5,6}) ~= {{1,4},{2,5},{3,6}} template auto zip (const ContainerT&... data) { - using container = std::tuple; - using iterator = std::tuple; + using IteratorT = std::tuple; return detail::zip::collection< - container, - iterator, + IteratorT, std::make_index_sequence - > ( - std::make_tuple (data...) - ); + > { + std::make_tuple (std::begin (data)...), + std::make_tuple (std::end (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. + /// + /// eg, util::izip ("abc") ~= {{0,'a'},{1,'b'},{2,'c'}} template auto izip (const ContainerT&... data) diff --git a/test/iterator.cpp b/test/iterator.cpp index 78760608..bc308415 100644 --- a/test/iterator.cpp +++ b/test/iterator.cpp @@ -14,12 +14,16 @@ main (int, char**) std::vector v_int { 1, 2, 3 }; std::array a_float { 1.1f, 2.2f, 3.3f }; + char c_char[] = { 'a', 'b', 'c' }; bool success = true; - for (auto [i, v, a]: util::izip (v_int, a_float)) { - success = success && v_int[i] == v && util::exactly_equal (a_float[i], a); + for (auto [i, v, a, c]: util::izip (v_int, a_float, c_char)) { + success = success && + v_int[i] == v && + util::exactly_equal (a_float[i], a) && + c_char[i] == c; } - tap.expect (success, "izip tuples of int and float"); + tap.expect (success, "izip containers of int, float, and char and an initialiser_list"); return tap.status (); }