Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <http://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | #pragma once |
||
8 | |||
9 | #include <iterator> |
||
10 | #include <tuple> |
||
11 | #include <type_traits> |
||
12 | #include "dxxsconf.h" |
||
13 | #include "ephemeral_range.h" |
||
14 | #include <utility> |
||
15 | |||
16 | namespace d_zip { |
||
17 | |||
18 | namespace detail { |
||
19 | |||
20 | template <typename... T> |
||
21 | void discard_arguments(T &&...) |
||
22 | { |
||
23 | } |
||
24 | |||
25 | template <std::size_t... N, typename... range_iterator_type> |
||
26 | void increment_iterator(std::tuple<range_iterator_type...> &iterator, std::index_sequence<N...>) |
||
27 | { |
||
28 | /* Order of evaluation is irrelevant, so pass the results to a |
||
29 | * discarding function. This permits the compiler to evaluate the |
||
30 | * expression elements in any order. |
||
31 | */ |
||
32 | discard_arguments(++(std::get<N>(iterator))...); |
||
33 | } |
||
34 | |||
35 | template <std::size_t... N, typename... range_iterator_type> |
||
36 | auto dereference_iterator(const std::tuple<range_iterator_type...> &iterator, std::index_sequence<N...>) |
||
37 | { |
||
38 | /* std::make_tuple is not appropriate here, because the result of |
||
39 | * dereferencing the iterator may be a reference, and the resulting |
||
40 | * tuple should store the reference, not the underlying object. |
||
41 | * Calling std::make_tuple would decay such references into the |
||
42 | * underlying type. |
||
43 | * |
||
44 | * std::tie is not appropriate here, because it captures all |
||
45 | * arguments as `T &`, so it fails to compile if the result of |
||
46 | * dereferencing the iterator is not a reference. |
||
47 | */ |
||
48 | return std::tuple< |
||
49 | decltype(*(std::get<N>(iterator)))... |
||
50 | >(*(std::get<N>(iterator))...); |
||
51 | } |
||
52 | |||
53 | } |
||
54 | |||
55 | } |
||
56 | |||
57 | /* This iterator terminates when the first zipped range terminates. The |
||
58 | * caller is responsible for ensuring that use of the zip_iterator does |
||
59 | * not increment past the end of any zipped range. This can be done by |
||
60 | * ensuring that the first zipped range is not longer than any other |
||
61 | * zipped range, or by ensuring that external logic stops the traversal |
||
62 | * before the zip_iterator increments past the end. |
||
63 | * |
||
64 | * There is no initialization time check that the below loop would be |
||
65 | * safe, since a check external to the zip_iterator could stop before |
||
66 | * undefined behaviour occurs. |
||
67 | |||
68 | for (auto i = zip_range.begin(), e = zip_range.end(); i != e; ++i) |
||
69 | { |
||
70 | if (condition()) |
||
71 | break; |
||
72 | } |
||
73 | |||
74 | */ |
||
75 | template <typename... range_iterator_type> |
||
76 | class zip_iterator : std::tuple<range_iterator_type...> |
||
77 | { |
||
78 | using base_type = std::tuple<range_iterator_type...>; |
||
79 | protected: |
||
80 | /* Prior to C++17, range-based for insisted on the same type for |
||
81 | * `begin` and `end`, so method `end_internal` must return a full iterator, |
||
82 | * even though most of it is a waste. To save some work, values that are |
||
83 | * used for ignored fields are default-constructed (if possible) |
||
84 | * instead of copy-constructed from the begin iterator. |
||
85 | */ |
||
86 | template <std::size_t I, typename T> |
||
87 | static typename std::enable_if<std::is_default_constructible<T>::value, T>::type end_construct_ignored_element() |
||
88 | { |
||
89 | return T(); |
||
90 | } |
||
91 | template <std::size_t I, typename T> |
||
92 | typename std::enable_if<!std::is_default_constructible<T>::value, T>::type end_construct_ignored_element() const |
||
93 | { |
||
94 | return std::get<I>(*this); |
||
95 | } |
||
96 | template <typename E0, std::size_t... N> |
||
97 | zip_iterator end_internal(const E0 &e0, std::index_sequence<0, N...>) const |
||
98 | { |
||
99 | return zip_iterator(e0, this->template end_construct_ignored_element<N, typename std::tuple_element<N, base_type>::type>()...); |
||
100 | } |
||
101 | using index_type = std::make_index_sequence<sizeof...(range_iterator_type)>; |
||
102 | public: |
||
103 | using base_type::base_type; |
||
104 | auto operator*() const |
||
105 | { |
||
106 | return d_zip::detail::dereference_iterator(*this, index_type()); |
||
107 | } |
||
108 | zip_iterator &operator++() |
||
109 | { |
||
110 | d_zip::detail::increment_iterator(*this, index_type()); |
||
111 | return *this; |
||
112 | } |
||
113 | bool operator!=(const zip_iterator &i) const |
||
114 | { |
||
115 | return std::get<0>(*this) != std::get<0>(i); |
||
116 | } |
||
117 | bool operator==(const zip_iterator &i) const |
||
118 | { |
||
119 | return !(*this != i); |
||
120 | } |
||
121 | }; |
||
122 | |||
123 | template <typename range0_iterator_type, typename... rangeN_iterator_type> |
||
124 | class zip : zip_iterator<range0_iterator_type, rangeN_iterator_type...> |
||
125 | { |
||
126 | range0_iterator_type m_end; |
||
127 | public: |
||
128 | using range_owns_iterated_storage = std::false_type; |
||
129 | using iterator = zip_iterator<range0_iterator_type, rangeN_iterator_type...>; |
||
130 | template <typename range0, typename... rangeN> |
||
131 | zip(range0 &&r0, rangeN &&... rN) : |
||
132 | iterator(std::begin(r0), std::begin(rN)...), m_end(std::end(r0)) |
||
133 | { |
||
134 | static_assert((!any_ephemeral_range<range0 &&, rangeN &&...>::value), "cannot zip storage of ephemeral ranges"); |
||
135 | } |
||
136 | __attribute_warn_unused_result |
||
137 | iterator begin() const { return *this; } |
||
138 | __attribute_warn_unused_result |
||
139 | iterator end() const |
||
140 | { |
||
141 | return this->end_internal(m_end, typename iterator::index_type()); |
||
142 | } |
||
143 | }; |
||
144 | |||
145 | template <typename range0, typename... rangeN> |
||
146 | zip(range0 &&r0, rangeN &&... rN) -> zip<decltype(std::begin(r0)), decltype(std::begin(rN))...>; |