Subversion Repositories Games.Descent

Rev

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))...>;