diff --git a/common/main/d_range.h b/common/main/d_range.h index 48df92202..e930b9fba 100644 --- a/common/main/d_range.h +++ b/common/main/d_range.h @@ -143,6 +143,10 @@ public: m_idx(i) { } + difference_type operator-(const xrange_iterator &i) const + { + return m_idx - i.m_idx; + } index_type operator*() const { return m_idx; diff --git a/common/main/d_zip.h b/common/main/d_zip.h index ed8ebf0ef..dec3e6a50 100644 --- a/common/main/d_zip.h +++ b/common/main/d_zip.h @@ -160,50 +160,72 @@ template requires(!element_selected) decltype(std::ignore) iterator_element_end_type(); +template +struct zip_sentinel : public std::tuple +{ + using std::tuple::tuple; +}; + /* Given as inputs: * - a selector mask * - a tuple of iterator types * - a std::index_sequence of the same length as the tuple * - * Produce a nested type named `type` defined as tuple where On=In if the - * iterator is selected in by the mask, and On=std::ignore if the iterator is - * not selected. The resulting tuple will then collapse down, avoiding the - * need to save non-selected types In in the end iterator. + * Produce a type defined as zip_sentinel where On=In if the iterator is + * selected in by the mask, and On=std::ignore if the iterator is not selected. + * The resulting tuple will then collapse down, avoiding the need to save + * non-selected types In in the end iterator. */ template requires(mask != zip_sequence_length_selector{} && sizeof...(range_index) == sizeof...(range_type)) -std::tuple::value, range_type>())...> iterator_end_type(std::tuple, std::index_sequence); +zip_sentinel::value, range_type>())...> iterator_end_type(std::tuple, std::index_sequence); template static constexpr auto capture_end_iterator(range &&r) { - if constexpr (std::is_same::value) + if constexpr (std::is_same::value) return std::ignore; else return std::ranges::end(r); } -template -static constexpr auto capture_end_iterators(const std::tuple *, range &&...r) +template +requires(sizeof...(N) == sizeof...(range)) +static constexpr auto capture_end_iterators(std::index_sequence, range &&...r) { - return std::tuple(capture_end_iterator(r)...); + return sentinel_type(capture_end_iterator(std::declval()))>(r)...); } -template -bool compare_zip_iterators(const zip_iterator &l, const zip_iterator &r, std::index_sequence) +template +static constexpr auto compare_zip_iterator(const zip_iterator &l, const zip_sentinel &r) { - /* For each member of the iterator, check if the member is excluded by the mask or - * if the member compares equal between the two instances. Excluded - * members are not checked for equality. When one iterator is `end()`, - * excluded members of that iterator may be default-constructed instead of - * set to the value of `rangeN.end()`, so examining such members would lead - * to incorrect results. - * - * Note the use of `||`, which is atypical for an equality comparison. By + if constexpr (mask) + /* For each included member of the iterator, check if the member compares + * equal between the two instances. Excluded members use the else + * block. + */ + return std::get(l) == std::get(r); + else + { + /* For each excluded member of the iterator, do nothing. Excluded members + * are not checked for equality, because excluded members are omitted from + * the sentinel, instead of being set to the value of `rangeN.end()`, + * so examining such members is not possible. + */ + (void)l; + (void)r; + return std::false_type{}; + } +} + +template +constexpr bool compare_zip_iterators(const zip_iterator &l, const zip_sentinel &r, std::index_sequence) +{ + /* Note the use of `||`, which is atypical for an equality comparison. By * design, a zip iterator should terminate when any of the component * sequences reaches its end, so use of `||` is correct for that purpose. */ - return ((examine_zip_element::value && std::get(l) == std::get(r)) || ... || false); + return (compare_zip_iterator::value, N>(l, r) || ... || false); } } @@ -235,64 +257,15 @@ bool compare_zip_iterators(const zip_iterator &l, const zip_iterator &r, std::in } */ -template -requires( - requires { - typename std::iterator_traits::difference_type; - } -) -class zip_iterator : std::tuple +template +class zip_iterator : std::tuple()))...> { - using base_type = std::tuple; - /* Prior to C++17, range-based for insisted on the same type for - * `begin` and `end`, so method `end_internal` must return a full iterator, - * even though most of it is a waste. To save some work, values that are - * used for ignored fields are default-constructed (if possible) - * instead of copy-constructed from the begin iterator. - * - * Even in C++20, many STL algorithms assume that `.begin()` and `.end()` - * produce the same type, so this class continues to produce a full-sized - * end iterator. - */ - template - requires(std::is_same::value && std::is_default_constructible::value) - static begin_element_type end_construct_element(const end_tuple_type &) - { - /* The type `begin_element_type` can be default-constructed, - * and the value will be ignored later. Use any valid instance of - * `begin_element_type`. The easiest such instance to get is a - * default-constructed value. - */ - return begin_element_type(); - } - template - requires(std::is_same::value && !std::is_default_constructible::value) - begin_element_type end_construct_element(const end_tuple_type &) const - { - /* The type cannot be default-constructed, but the value will be - * ignored. Return a copy from the begin iterator, because the end - * iterator did not retain a value for this index. - */ - return std::get(*this); - } - template - requires(!std::is_same::value) - static begin_element_type end_construct_element(const end_tuple_type &end_iter) - { - /* The value will not be ignored, so a valid value must be provided - * from the end iterator. - */ - return std::get(end_iter); - } + using base_type = std::tuple()))...>; protected: - template - zip_iterator end_internal(const end_tuple_type &end_iter, std::index_sequence) const - { - return zip_iterator(this->template end_construct_element::type, typename std::tuple_element::type, end_tuple_type>(end_iter)...); - } - using index_sequence_type = std::make_index_sequence<1 + sizeof...(rangeN_iterator_type)>; + using index_sequence_type = std::make_index_sequence; + using sentinel_type = decltype(d_zip::detail::iterator_end_type(std::declval>(), index_sequence_type())); public: - using difference_type = typename std::iterator_traits::difference_type; + using difference_type = decltype(std::get<0>(std::declval()) - std::get<0>(std::declval())); using index_type = range_index_type; using value_type = decltype(d_zip::detail::dereference_iterator(std::declval(), index_sequence_type())); using pointer = value_type *; @@ -317,13 +290,13 @@ public: d_zip::detail::increment_iterator(*this, index_sequence_type()); return result; } - difference_type operator-(const zip_iterator &i) const + auto operator-(const zip_iterator &i) const { - return std::get<0>(*this) - std::get<0>(i); + return std::get<0>(static_cast(*this)) - std::get<0>(static_cast(i)); } - bool operator==(const zip_iterator &i) const + constexpr bool operator==(const sentinel_type &i) const { - return d_zip::detail::compare_zip_iterators(*this, i, index_sequence_type()); + return d_zip::detail::compare_zip_iterators(*this, i, index_sequence_type()); } }; @@ -367,14 +340,17 @@ requires( std::tuple()))...> > ) -class zip : zip_iterator()))...> +class zip : zip_iterator { - decltype(d_zip::detail::iterator_end_type(std::declval>(), std::declval>())) m_end; public: - using iterator = zip_iterator()))...>; + using iterator = zip_iterator; +private: + using typename iterator::sentinel_type; + sentinel_type m_end; +public: using typename iterator::index_type; constexpr zip(rangeN_type &&... rN) : - iterator(std::begin(rN)...), m_end(d_zip::detail::capture_end_iterators(static_cast(nullptr), rN...)) + iterator(std::begin(rN)...), m_end(d_zip::detail::capture_end_iterators(typename iterator::index_sequence_type(), rN...)) { } template @@ -385,9 +361,9 @@ public: [[nodiscard]] iterator begin() const { return *this; } [[nodiscard]] - iterator end() const + auto end() const { - return this->end_internal(m_end, typename iterator::index_sequence_type()); + return m_end; } };