iterator/zip: rewrite for simplicity and forwarding support

This commit is contained in:
Danny Robson 2020-02-25 16:17:04 +11:00
parent 58b42d5d9b
commit f1aa7fa775

View File

@ -19,17 +19,21 @@ namespace cruft::iterator {
namespace detail::zip { namespace detail::zip {
/// A container that holds multiple iterators and returns a tuple of /// A container that holds multiple iterators and returns a tuple of
/// their results when dereferenced. /// their results when dereferenced.
///
/// \tparam IteratorT A tuple-like object that contains iterators
template < template <
typename IteratorT, typename IteratorT,
typename = std::make_index_sequence<std::tuple_size_v<IteratorT>> typename = std::make_index_sequence<std::tuple_size_v<IteratorT>>
> >
struct zipped_iterator; class zipped_iterator;
template <typename IteratorT, std::size_t ...Indices> template <
struct zipped_iterator<IteratorT, std::index_sequence<Indices...>> { typename IteratorT,
std::size_t ...IndexV
>
class zipped_iterator<
IteratorT,
std::index_sequence<IndexV...>
> {
public: public:
// We can't declare ourselves as a forward_iterator because we're // We can't declare ourselves as a forward_iterator because we're
// unable to supply references to our value_type when we get // unable to supply references to our value_type when we get
@ -41,25 +45,22 @@ namespace cruft::iterator {
using iterator_category = std::input_iterator_tag; using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using value_type = std::tuple< using value_type = std::tuple<
decltype( typename std::iterator_traits<
*std::get<Indices> ( std::tuple_element_t<IndexV, IteratorT>
std::declval<IteratorT> () >::reference...
)
)...
>; >;
using reference = value_type; using reference = value_type;
using pointer = value_type*; using pointer = value_type*;
zipped_iterator (IteratorT _iterators)
zipped_iterator (IteratorT _iterators): : m_iterators (std::move (_iterators))
m_iterators (_iterators)
{ ; } { ; }
zipped_iterator& zipped_iterator&
operator++ (void) operator++ (void)
{ {
(++std::get<Indices> (m_iterators), ...); (++std::get<IndexV> (m_iterators), ...);
return *this; return *this;
} }
@ -70,14 +71,8 @@ namespace cruft::iterator {
decltype(auto) decltype(auto)
operator* (void) operator* (void)
{ {
// We deliberately construct a tuple manually here to reduce return std::forward_as_tuple (
// the risk of dangling references. `forward_as_tuple` and *std::get<IndexV> (m_iterators)...
// `make_tuple` have resulted in references to locals in the
// past.
return std::tuple<
decltype(*std::get<Indices> (m_iterators))...
> (
*std::get<Indices> (m_iterators)...
); );
} }
@ -85,27 +80,21 @@ namespace cruft::iterator {
decltype (auto) decltype (auto)
operator* (void) const operator* (void) const
{ {
// We deliberately construct a tuple manually here to reduce return std::forward_as_tuple (
// the risk of dangling references. `forward_as_tuple` and *std::get<IndexV> (m_iterators)...
// `make_tuple` have resulted in references to locals in the
// past.
return std::tuple<
decltype(*std::get<Indices> (m_iterators))...
> (
*std::get<Indices> (m_iterators)...
); );
} }
bool bool
operator== (const zipped_iterator &rhs) const operator== (zipped_iterator const &rhs) const
{ {
return m_iterators == rhs.m_iterators; return m_iterators == rhs.m_iterators;
} }
bool bool
operator!= (const zipped_iterator &rhs) const operator!= (zipped_iterator const &rhs) const
{ {
return !(*this == rhs); return !(*this == rhs);
} }
@ -116,6 +105,10 @@ namespace cruft::iterator {
}; };
template <typename TupleT>
zipped_iterator (TupleT) -> zipped_iterator<TupleT>;
/// A simple container for multiple collections that returns a /// A simple container for multiple collections that returns a
/// wrapped multi-iterator for begin and end. /// wrapped multi-iterator for begin and end.
/// ///
@ -124,46 +117,64 @@ namespace cruft::iterator {
/// ///
/// \tparam StoreT A tuple-like object of collections (or references /// \tparam StoreT A tuple-like object of collections (or references
/// to collections). /// to collections).
template <typename ...StoreT> template <
class collection { typename StoreT,
typename = std::make_index_sequence<
std::tuple_size_v<StoreT>
>
>
class collection;
template <
typename StoreT,
std::size_t ...IndexV
> class collection<
StoreT,
std::index_sequence<IndexV...>
> {
public: public:
collection (StoreT&&... _store): collection (StoreT _store)
m_store (std::forward<StoreT> (_store)...) : m_store (std::move (_store))
{ ; } { ; }
using inner_t = std::tuple<decltype(std::begin (std::declval<StoreT> ()))...>;
using indices_t = std::make_index_sequence<sizeof...(StoreT)>;
using iterator = zipped_iterator<inner_t, indices_t>; auto begin (void)&
iterator begin (void)&
{ {
return iterator ( return zipped_iterator (
tuple::value::map (::cruft::functor::begin {}, m_store) std::tuple (
std::begin (
std::get<IndexV> (m_store)
)...
)
); );
} }
iterator begin (void) const& auto begin (void) const&
{ {
return iterator ( return zipped_iterator (
tuple::value::map (::cruft::functor::begin {}, m_store) std::tuple (
std::begin (std::get<IndexV> (m_store))...
)
); );
} }
iterator end (void)& auto end (void)&
{ {
return iterator ( return zipped_iterator (
tuple::value::map (cruft::functor::end {}, m_store) std::tuple (
std::end (std::get<IndexV> (m_store))...
)
); );
} }
iterator end (void) const & auto end (void) const &
{ {
return iterator ( return zipped_iterator (
tuple::value::map (cruft::functor::end {}, m_store) std::tuple (
std::end (std::get<IndexV> (m_store))...
)
); );
} }
@ -177,9 +188,8 @@ namespace cruft::iterator {
return std::size (std::get<0> (m_store)); return std::size (std::get<0> (m_store));
} }
private: private:
std::tuple<StoreT...> m_store; StoreT m_store;
}; };
} }
@ -200,8 +210,10 @@ namespace cruft::iterator {
{ {
CHECK (((std::size (data) == std::size (variadic::get<0> (data...))) && ...)); CHECK (((std::size (data) == std::size (variadic::get<0> (data...))) && ...));
return detail::zip::collection<ContainerT...> ( return detail::zip::collection<std::tuple<ContainerT...>> (
std::forward<ContainerT> (data)... std::forward_as_tuple (
std::forward<ContainerT> (data)...
)
); );
} }