OXYGENHUB
◆ main openmsx / src / utils / xrange.hh
3.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef XRANGE_HH
2
#define XRANGE_HH
3
4
// Utility to iterate over a range of numbers,
5
// modeled after python's xrange() function.
6
//
7
// Typically used as a more compact notation for some for-loop patterns:
8
//
9
// a) loop over [0, N)
10
//
11
//   auto num = expensiveFunctionCall();
12
//   for (decltype(num) i = 0; i < num; ++i) { ... }
13
//
14
//   for (auto i : xrange(expensiveFunctionCall()) { ... }
15
//
16
// b) loop over [B, E)
17
//
18
//   auto i   = func1();
19
//   auto end = func2();
20
//   for (/**/; i < end; ++i) { ... }
21
//
22
//   for (auto i : xrange(func1(), func2()) { ... }
23
//
24
// Note that when using the xrange utility you also don't need to worry about
25
// the correct type of the induction variable.
26
//
27
// Gcc is able to optimize the xrange() based loops to the same code as the
28
// equivalent 'old-style' for loop.
29
//
30
// In an earlier version of this utility I also implemented the possibility to
31
// specify a step size (currently the step size is always +1). Although I
32
// believe that code was correct I still removed it because it was quite tricky
33
// (getting the stop condition correct is not trivial) and we don't need it
34
// currently.
35
36
#include "narrow.hh"
37
#include <cstddef>
38
#include <iterator>
39
#include <type_traits>
40
41
template<typename T> struct XRange
42
{
43
	struct Iter
44
	{
45
		using difference_type = ptrdiff_t;
46
		using value_type = T;
47
		using pointer    = T*;
48
		using reference  = T&;
49
		using iterator_category = std::random_access_iterator_tag;
50
51
		// ForwardIterator
52
		[[nodiscard]] constexpr T operator*() const
53
		{
54
			return x;
55
		}
56
57
		constexpr Iter& operator++()
58
		{
59
			++x;
60
			return *this;
61
		}
62
		constexpr Iter operator++(int)
63
		{
64
			auto copy = *this;
65
			++x;
66
			return copy;
67
		}
68
69
		// BidirectionalIterator
70
		constexpr Iter& operator--()
71
		{
72
			--x;
73
			return *this;
74
		}
75
		constexpr Iter operator--(int)
76
		{
77
			auto copy = *this;
78
			--x;
79
			return copy;
80
		}
81
82
		// RandomAccessIterator
83
		constexpr Iter& operator+=(difference_type n)
84
		{
85
			x += narrow<T>(n);
86
			return *this;
87
		}
88
		constexpr Iter& operator-=(difference_type n)
89
		{
90
			x -= narrow<T>(n);
91
			return *this;
92
		}
93
94
		[[nodiscard]] constexpr friend Iter operator+(Iter i, difference_type n)
95
		{
96
			i += n;
97
			return i;
98
		}
99
		[[nodiscard]] constexpr friend Iter operator+(difference_type n, Iter i)
100
		{
101
			i += n;
102
			return i;
103
		}
104
		[[nodiscard]] constexpr friend Iter operator-(Iter i, difference_type n)
105
		{
106
			i -= n;
107
			return i;
108
		}
109
110
		[[nodiscard]] constexpr friend difference_type operator-(const Iter& i, const Iter& j)
111
		{
112
			return i.x - j.x;
113
		}
114
115
		[[nodiscard]] constexpr T operator[](difference_type n)
116
		{
117
			return *(*this + n);
118
		}
119
120
		[[nodiscard]] /*constexpr*/ auto operator<=>(const Iter&) const = default;
121
122
		T x;
123
	};
124
125
	[[nodiscard]] constexpr auto begin() const { return Iter{b}; }
126
	[[nodiscard]] constexpr auto end()   const { return Iter{e}; }
127
128
	/*const*/ T b; // non-const to workaround msvc bug(?)
129
	/*const*/ T e;
130
};
131
132
template<typename T> [[nodiscard]] constexpr auto xrange(T e)
133
{
134
	return XRange<T>{T(0), e < T(0) ? T(0) : e};
135
}
136
template<typename T1, typename T2> [[nodiscard]] constexpr auto xrange(T1 b, T2 e)
137
{
138
	static_assert(std::is_signed_v<T1> == std::is_signed_v<T2>);
139
	using T = std::common_type_t<T1, T2>;
140
	return XRange<T>{b, e < b ? b : e};
141
}
142
143
144
/** Repeat the given operation 'op' 'n' times.
145
 */
146
template<typename T, typename Op>
147
constexpr void repeat(T n, Op op)
148
{
149
    for (auto i : xrange(n)) {
150
        (void)i;
151
        op();
152
    }
153
}
154
155
#endif