/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2010-2018 Danny Robson */ #ifndef CRUFT_UTIL_ITERATOR_HPP #define CRUFT_UTIL_ITERATOR_HPP #include "types/traits.hpp" #include "variadic.hpp" #include "view.hpp" #include template class referencing_iterator { protected: typedef typename std::enable_if< is_dereferencable< typename Base::value_type >::value, typename Base::value_type >::type base_value_type; public: typedef typename dereferenced_type::type value_type ; typedef typename Base::difference_type difference_type ; typedef value_type& reference ; typedef value_type* pointer; typedef typename Base::iterator_category iterator_category; protected: Base m_base; public: explicit referencing_iterator (Base _base): m_base (_base) { ; } referencing_iterator& operator++() { ++m_base; return *this; } referencing_iterator operator++(int) { auto val = *this; ++m_base; return val; } bool operator== (const referencing_iterator &rhs) { return m_base == rhs.m_base; } bool operator!= (const referencing_iterator &rhs) { return m_base != rhs.m_base; } bool operator>= (const referencing_iterator &rhs) { return m_base >= rhs.m_base; } bool operator<= (const referencing_iterator &rhs) { return m_base <= rhs.m_base; } bool operator> (const referencing_iterator &rhs) { return m_base > rhs.m_base; } bool operator< (const referencing_iterator &rhs) { return m_base < rhs.m_base; } const value_type& operator*() const { return **m_base; } reference operator*() { return **m_base; } difference_type operator-(const referencing_iterator& rhs) const { return m_base - rhs.m_base; } referencing_iterator operator-(int rhs) const { return referencing_iterator (m_base - rhs); } referencing_iterator operator+(int rhs) const { return referencing_iterator (m_base + rhs); } }; namespace util { /////////////////////////////////////////////////////////////////////////// /// an output iterator that inserts a delimiter between successive /// assignments /// /// very useful for outputting comma seperated lists to an ostream, eg: /// /// std::copy ( /// std::cbegin (container), /// std::cend (container), /// util::infix_iterator (os, ", ") /// ); template < typename T, class CharT = char, class Traits = std::char_traits > class infix_iterator : public std::iterator { public: using char_type = CharT; using traits_type = Traits; using ostream_type = std::basic_ostream; infix_iterator (ostream_type& _output, const CharT *_delimiter): m_output (_output), m_delimiter (_delimiter) { ; } infix_iterator& operator= (const T &value) { if (!m_first) m_output << m_delimiter; m_output << value; m_first = false; return *this; } infix_iterator& operator* (void) { return *this; } infix_iterator& operator++ (void) { return *this; } infix_iterator& operator++ (int) { return *this; } private: bool m_first = true; ostream_type &m_output; const CharT *m_delimiter; }; namespace detail { template struct infix_t { const ContainerT &_container; const CharT *_delimiter; }; template std::ostream& operator<< (std::ostream &os, const infix_t &val) { std::copy (std::cbegin (val._container), std::cend (val._container), infix_iterator (os, val._delimiter)); return os; } }; /// a helper function that returns an object that will use a /// util::infix_iterator to output a container's values to an ostream with /// the given delimiter. /// /// reduces boilerplate code required to output lists of things /// /// std::cout << util::make_infix (container) << '\n'; template auto make_infix (const ContainerT &_container, const CharT *_delimiter = ", ") { return detail::infix_t { _container, _delimiter }; }; /////////////////////////////////////////////////////////////////////////// // template struct numeric_iterator : public std::iterator< typename std::iterator_traits::iterator_category, decltype (+std::declval::value_type> ()), typename std::iterator_traits::difference_type, typename std::iterator_traits::pointer, typename std::iterator_traits::reference > { static_assert (std::is_arithmetic_v::value_type>); explicit numeric_iterator (IteratorT _inner): m_inner (_inner) { ; } auto operator++ (void) { ++m_inner; return *this; } auto operator- (const numeric_iterator &rhs) const { return typename std::iterator_traits::difference_type { m_inner - rhs.m_inner }; } auto operator* (void) const { return +*m_inner; } auto operator== (const numeric_iterator &rhs) const { return m_inner == rhs.m_inner; } auto operator!= (const numeric_iterator &rhs) const { return m_inner != rhs.m_inner; } private: IteratorT m_inner; }; //------------------------------------------------------------------------- // convenience function that constructs a view of numeric_iterators for a // provided container template auto numeric_view (ContainerT &data) { return util::view { numeric_iterator (std::begin (data)), numeric_iterator (std::end (data)) }; } //------------------------------------------------------------------------- template auto numeric_view (const ContainerT &data) { return util::view { numeric_iterator (std::begin (data)), numeric_iterator (std::end (data)) }; } /////////////////////////////////////////////////////////////////////////// template class indices { public: indices (const ContainerT &_container): m_container (_container) { ; } class iterator : public std::iterator { public: iterator (std::size_t _index): m_index (_index) { ; } bool operator!= (const iterator &rhs) const { return m_index != rhs.m_index; } bool operator== (const iterator &rhs) const { return m_index == rhs.m_index; } iterator& operator++ (void) & { ++m_index; return *this; }; const std::size_t& operator* (void) const& { return m_index; } private: std::size_t m_index; }; iterator begin (void) const { return iterator { 0 }; } iterator end (void) const { return iterator { m_container.size () }; } private: const ContainerT &m_container; }; //------------------------------------------------------------------------- template indices make_indices (const T &_t) { return indices (_t); } /////////////////////////////////////////////////////////////////////////// namespace detail::zip { template < typename IteratorT, typename = std::make_index_sequence> > struct iterator; template struct iterator> { public: // we cannot be a forward iterator because we don't want to supply // references to a value type as that would necessitate storing // said value types within the iterator. using iterator_category = std::input_iterator_tag; using difference_type = std::ptrdiff_t; iterator (IteratorT _iterators): m_iterators (_iterators) { ; } iterator& operator++ (void) { std::tuple (++std::get (m_iterators)...); return *this; } iterator operator++ (int); auto operator* (void) { return std::forward_as_tuple (*std::get (m_iterators)...); } bool operator== (const iterator &rhs) const { return m_iterators == rhs.m_iterators; } bool operator!= (const iterator &rhs) const { return !(*this == rhs); } private: IteratorT m_iterators; }; // 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. // // we have to store begin and end iterators because we might not have // enough information to determine the correct types from StoreT; // eg, in the case of arrays that have decayed to pointers we can't // find the end. // // BeginT: a tuple of begin iterators across all containers // // EndT: a tuple of end iterators across all containers // // StoreT: a tuple of containers we might own. used when we were // provided with an rval at zip time. allows us to destroy the // data when we're actually done iterating. template < typename StoreT, typename BeginT, typename EndT, typename I = std::make_index_sequence> > class collection; //--------------------------------------------------------------------- template < typename StoreT, typename BeginT, typename EndT, std::size_t ...I > class collection< StoreT, BeginT, EndT, std::index_sequence > { public: collection (StoreT _store, BeginT _begin, EndT _end): m_store { std::move (_store) }, m_begin (std::move (_begin)), m_end (std::move (_end)) { ; } auto begin (void)& { return iterator { m_begin }; } auto end (void)& { return iterator { m_end }; } private: StoreT m_store; BeginT m_begin; EndT 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 (ContainerT&&... data) { return detail::zip::collection< decltype (std::forward_as_tuple (data...)), decltype (std::make_tuple (std::begin (data)...)), decltype (std::make_tuple (std::end (data)...)), std::make_index_sequence > ( std::forward_as_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 (ContainerT&&... data) { return zip ( ::util::make_indices (::util::variadic::get<0> (data...)), std::forward (data)... ); } /////////////////////////////////////////////////////////////////////////// /// an output iterator that always discards any parameters on assignment. /// /// sometimes useful to pass to algorithms that generate useful results as /// a return value, while not caring about the implicit OutputIterator /// results. struct discard_iterator : public std::iterator { template void operator= (const T&) { ; } discard_iterator& operator++ ( ) { return *this; } discard_iterator operator++ (int) { return *this; } discard_iterator& operator* ( ) { return *this; } }; /////////////////////////////////////////////////////////////////////////// /// an iterator that can be infinitely incremented but never assigned. /// /// useful for iterator ranges where the begin iterator is an output /// iterator and hence never reaches an end point (and where we don't want /// to engineer the client code to account for this). template < typename ValueT, typename CategoryT, typename DistanceT, typename PointerT, typename ReferenceT > struct unequal_iterator { using value_type = ValueT; using iterator_category = CategoryT; using difference_type = DistanceT; using pointer = PointerT; using reference = ReferenceT; unequal_iterator& operator++ ( ) { return *this; } unequal_iterator operator++ (int) { return *this; } }; //------------------------------------------------------------------------- template auto make_unequal_iterator (const ContainerT&) { using t = typename std::iterator_traits; return unequal_iterator< typename t::value_type, typename t::iterator_category, typename t::difference_type, typename t::pointer, typename t::reference > {}; }; //------------------------------------------------------------------------- template < typename OtherT, typename ValueT, typename CategoryT, typename DistanceT, typename PointerT, typename ReferenceT> constexpr bool operator== ( const unequal_iterator&, const OtherT& ) { return false; } //------------------------------------------------------------------------- template < typename OtherT, typename ValueT, typename CategoryT, typename DistanceT, typename PointerT, typename ReferenceT> constexpr bool operator== ( const OtherT&, const unequal_iterator& ) { return false; } /////////////////////////////////////////////////////////////////////////// template OutputIt _transform_by_block ( const util::view &, OutputIt cursor, FunctionT && ) { return cursor; } //------------------------------------------------------------------------- template OutputIt _transform_by_block ( const util::view &dst, OutputIt cursor, FunctionT &&func, const InputT &_src, TailT &&...tail ) { auto remain = _src; if (cursor != dst.begin ()) { auto infill = std::distance (cursor, dst.end ()); if (remain.size () < static_cast (infill)) { return _transform_by_block ( dst, std::copy_n (remain.begin (), remain.size (), cursor), std::forward (func), std::forward (tail)... ); } std::copy_n (remain.begin (), infill, cursor); func (dst); cursor = dst.begin (); remain = { remain.begin () + infill, remain.end () }; } while (remain.size () >= dst.size ()) { std::copy_n (remain.begin (), dst.size (), dst.begin ()); func (dst); remain = { remain.begin () + dst.size (), remain.end () }; } return _transform_by_block ( dst, std::copy (remain.begin (), remain.end (), cursor), std::forward (func), std::forward (tail)... ); } //------------------------------------------------------------------------- template OutputIt transform_by_block (const util::view &dst, FunctionT &&func, Args &&...src) { return _transform_by_block ( dst, dst.begin (), std::forward (func), std::forward (src)... ); } }; #endif