array-view.h revision 1.1.1.2 1 1.1.1.2 christos /* Copyright (C) 2017-2023 Free Software Foundation, Inc.
2 1.1 christos
3 1.1 christos This file is part of GDB.
4 1.1 christos
5 1.1 christos This program is free software; you can redistribute it and/or modify
6 1.1 christos it under the terms of the GNU General Public License as published by
7 1.1 christos the Free Software Foundation; either version 3 of the License, or
8 1.1 christos (at your option) any later version.
9 1.1 christos
10 1.1 christos This program is distributed in the hope that it will be useful,
11 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
12 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 1.1 christos GNU General Public License for more details.
14 1.1 christos
15 1.1 christos You should have received a copy of the GNU General Public License
16 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 1.1 christos
18 1.1 christos #ifndef COMMON_ARRAY_VIEW_H
19 1.1 christos #define COMMON_ARRAY_VIEW_H
20 1.1 christos
21 1.1 christos #include "traits.h"
22 1.1.1.2 christos #include <algorithm>
23 1.1 christos #include <type_traits>
24 1.1 christos
25 1.1 christos /* An array_view is an abstraction that provides a non-owning view
26 1.1 christos over a sequence of contiguous objects.
27 1.1 christos
28 1.1 christos A way to put it is that array_view is to std::vector (and
29 1.1 christos std::array and built-in arrays with rank==1) like std::string_view
30 1.1 christos is to std::string.
31 1.1 christos
32 1.1 christos The main intent of array_view is to use it as function input
33 1.1 christos parameter type, making it possible to pass in any sequence of
34 1.1 christos contiguous objects, irrespective of whether the objects live on the
35 1.1 christos stack or heap and what actual container owns them. Implicit
36 1.1 christos construction from the element type is supported too, making it easy
37 1.1 christos to call functions that expect an array of elements when you only
38 1.1 christos have one element (usually on the stack). For example:
39 1.1 christos
40 1.1 christos struct A { .... };
41 1.1 christos void function (gdb::array_view<A> as);
42 1.1 christos
43 1.1 christos std::vector<A> std_vec = ...;
44 1.1 christos std::array<A, N> std_array = ...;
45 1.1 christos A array[] = {...};
46 1.1 christos A elem;
47 1.1 christos
48 1.1 christos function (std_vec);
49 1.1 christos function (std_array);
50 1.1 christos function (array);
51 1.1 christos function (elem);
52 1.1 christos
53 1.1 christos Views can be either mutable or const. A const view is simply
54 1.1 christos created by specifying a const T as array_view template parameter,
55 1.1 christos in which case operator[] of non-const array_view objects ends up
56 1.1 christos returning const references. Making the array_view itself const is
57 1.1 christos analogous to making a pointer itself be const. I.e., disables
58 1.1 christos re-seating the view/pointer.
59 1.1 christos
60 1.1 christos Since array_view objects are small (pointer plus size), and
61 1.1 christos designed to be trivially copyable, they should generally be passed
62 1.1 christos around by value.
63 1.1 christos
64 1.1 christos You can find unit tests covering the whole API in
65 1.1 christos unittests/array-view-selftests.c. */
66 1.1 christos
67 1.1 christos namespace gdb {
68 1.1 christos
69 1.1 christos template <typename T>
70 1.1 christos class array_view
71 1.1 christos {
72 1.1 christos /* True iff decayed T is the same as decayed U. E.g., we want to
73 1.1 christos say that 'T&' is the same as 'const T'. */
74 1.1 christos template <typename U>
75 1.1 christos using IsDecayedT = typename std::is_same<typename std::decay<T>::type,
76 1.1 christos typename std::decay<U>::type>;
77 1.1 christos
78 1.1 christos /* True iff decayed T is the same as decayed U, and 'U *' is
79 1.1 christos implicitly convertible to 'T *'. This is a requirement for
80 1.1 christos several methods. */
81 1.1 christos template <typename U>
82 1.1 christos using DecayedConvertible = gdb::And<IsDecayedT<U>,
83 1.1 christos std::is_convertible<U *, T *>>;
84 1.1 christos
85 1.1 christos public:
86 1.1 christos using value_type = T;
87 1.1 christos using reference = T &;
88 1.1 christos using const_reference = const T &;
89 1.1 christos using size_type = size_t;
90 1.1 christos
91 1.1 christos /* Default construction creates an empty view. */
92 1.1 christos constexpr array_view () noexcept
93 1.1 christos : m_array (nullptr), m_size (0)
94 1.1 christos {}
95 1.1 christos
96 1.1 christos /* Create an array view over a single object of the type of an
97 1.1 christos array_view element. The created view as size==1. This is
98 1.1 christos templated on U to allow constructing a array_view<const T> over a
99 1.1 christos (non-const) T. The "convertible" requirement makes sure that you
100 1.1 christos can't create an array_view<T> over a const T. */
101 1.1 christos template<typename U,
102 1.1 christos typename = Requires<DecayedConvertible<U>>>
103 1.1 christos constexpr array_view (U &elem) noexcept
104 1.1 christos : m_array (&elem), m_size (1)
105 1.1 christos {}
106 1.1 christos
107 1.1 christos /* Same as above, for rvalue references. */
108 1.1 christos template<typename U,
109 1.1 christos typename = Requires<DecayedConvertible<U>>>
110 1.1 christos constexpr array_view (U &&elem) noexcept
111 1.1 christos : m_array (&elem), m_size (1)
112 1.1 christos {}
113 1.1 christos
114 1.1 christos /* Create an array view from a pointer to an array and an element
115 1.1 christos count. */
116 1.1 christos template<typename U,
117 1.1 christos typename = Requires<DecayedConvertible<U>>>
118 1.1 christos constexpr array_view (U *array, size_t size) noexcept
119 1.1 christos : m_array (array), m_size (size)
120 1.1 christos {}
121 1.1 christos
122 1.1 christos /* Create an array view from a range. This is templated on both U
123 1.1 christos an V to allow passing in a mix of 'const T *' and 'T *'. */
124 1.1 christos template<typename U, typename V,
125 1.1 christos typename = Requires<DecayedConvertible<U>>,
126 1.1 christos typename = Requires<DecayedConvertible<V>>>
127 1.1 christos constexpr array_view (U *begin, V *end) noexcept
128 1.1 christos : m_array (begin), m_size (end - begin)
129 1.1 christos {}
130 1.1 christos
131 1.1 christos /* Create an array view from an array. */
132 1.1 christos template<typename U, size_t Size,
133 1.1 christos typename = Requires<DecayedConvertible<U>>>
134 1.1 christos constexpr array_view (U (&array)[Size]) noexcept
135 1.1 christos : m_array (array), m_size (Size)
136 1.1 christos {}
137 1.1 christos
138 1.1 christos /* Create an array view from a contiguous container. E.g.,
139 1.1 christos std::vector and std::array. */
140 1.1 christos template<typename Container,
141 1.1 christos typename = Requires<gdb::Not<IsDecayedT<Container>>>,
142 1.1 christos typename
143 1.1.1.2 christos = Requires<DecayedConvertible
144 1.1.1.2 christos <typename std::remove_pointer
145 1.1.1.2 christos <decltype (std::declval<Container> ().data ())
146 1.1.1.2 christos >::type>>,
147 1.1 christos typename
148 1.1 christos = Requires<std::is_convertible
149 1.1 christos <decltype (std::declval<Container> ().size ()),
150 1.1 christos size_type>>>
151 1.1 christos constexpr array_view (Container &&c) noexcept
152 1.1 christos : m_array (c.data ()), m_size (c.size ())
153 1.1 christos {}
154 1.1 christos
155 1.1 christos /* Observer methods. Some of these can't be constexpr until we
156 1.1 christos require C++14. */
157 1.1 christos /*constexpr14*/ T *data () noexcept { return m_array; }
158 1.1 christos constexpr const T *data () const noexcept { return m_array; }
159 1.1 christos
160 1.1 christos /*constexpr14*/ T *begin () noexcept { return m_array; }
161 1.1 christos constexpr const T *begin () const noexcept { return m_array; }
162 1.1 christos
163 1.1 christos /*constexpr14*/ T *end () noexcept { return m_array + m_size; }
164 1.1 christos constexpr const T *end () const noexcept { return m_array + m_size; }
165 1.1 christos
166 1.1 christos /*constexpr14*/ reference operator[] (size_t index) noexcept
167 1.1.1.2 christos {
168 1.1.1.2 christos #if defined(_GLIBCXX_DEBUG)
169 1.1.1.2 christos gdb_assert (index < m_size);
170 1.1.1.2 christos #endif
171 1.1.1.2 christos return m_array[index];
172 1.1.1.2 christos }
173 1.1 christos constexpr const_reference operator[] (size_t index) const noexcept
174 1.1.1.2 christos {
175 1.1.1.2 christos #if defined(_GLIBCXX_DEBUG) && __cplusplus >= 201402L
176 1.1.1.2 christos gdb_assert (index < m_size);
177 1.1.1.2 christos #endif
178 1.1.1.2 christos return m_array[index];
179 1.1.1.2 christos }
180 1.1 christos
181 1.1 christos constexpr size_type size () const noexcept { return m_size; }
182 1.1 christos constexpr bool empty () const noexcept { return m_size == 0; }
183 1.1 christos
184 1.1 christos /* Slice an array view. */
185 1.1 christos
186 1.1 christos /* Return a new array view over SIZE elements starting at START. */
187 1.1 christos constexpr array_view<T> slice (size_type start, size_type size) const noexcept
188 1.1.1.2 christos {
189 1.1.1.2 christos #if defined(_GLIBCXX_DEBUG) && __cplusplus >= 201402L
190 1.1.1.2 christos gdb_assert (start + size <= m_size);
191 1.1.1.2 christos #endif
192 1.1.1.2 christos return {m_array + start, size};
193 1.1.1.2 christos }
194 1.1 christos
195 1.1 christos /* Return a new array view over all the elements after START,
196 1.1 christos inclusive. */
197 1.1 christos constexpr array_view<T> slice (size_type start) const noexcept
198 1.1.1.2 christos {
199 1.1.1.2 christos #if defined(_GLIBCXX_DEBUG) && __cplusplus >= 201402L
200 1.1.1.2 christos gdb_assert (start <= m_size);
201 1.1.1.2 christos #endif
202 1.1.1.2 christos return {m_array + start, size () - start};
203 1.1.1.2 christos }
204 1.1 christos
205 1.1 christos private:
206 1.1 christos T *m_array;
207 1.1 christos size_type m_size;
208 1.1 christos };
209 1.1 christos
210 1.1.1.2 christos /* Copy the contents referenced by the array view SRC to the array view DEST.
211 1.1.1.2 christos
212 1.1.1.2 christos The two array views must have the same length. */
213 1.1.1.2 christos
214 1.1.1.2 christos template <typename U, typename T>
215 1.1.1.2 christos void copy (gdb::array_view<U> src, gdb::array_view<T> dest)
216 1.1.1.2 christos {
217 1.1.1.2 christos gdb_assert (dest.size () == src.size ());
218 1.1.1.2 christos if (dest.data () < src.data ())
219 1.1.1.2 christos std::copy (src.begin (), src.end (), dest.begin ());
220 1.1.1.2 christos else if (dest.data () > src.data ())
221 1.1.1.2 christos std::copy_backward (src.begin (), src.end (), dest.end ());
222 1.1.1.2 christos }
223 1.1.1.2 christos
224 1.1 christos /* Compare LHS and RHS for (deep) equality. That is, whether LHS and
225 1.1 christos RHS have the same sizes, and whether each pair of elements of LHS
226 1.1 christos and RHS at the same position compares equal. */
227 1.1 christos
228 1.1 christos template <typename T>
229 1.1 christos bool
230 1.1 christos operator== (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs)
231 1.1 christos {
232 1.1 christos if (lhs.size () != rhs.size ())
233 1.1 christos return false;
234 1.1 christos
235 1.1 christos for (size_t i = 0; i < lhs.size (); i++)
236 1.1 christos if (!(lhs[i] == rhs[i]))
237 1.1 christos return false;
238 1.1 christos
239 1.1 christos return true;
240 1.1 christos }
241 1.1 christos
242 1.1 christos /* Compare two array_views for inequality. */
243 1.1 christos
244 1.1 christos template <typename T>
245 1.1 christos bool
246 1.1 christos operator!= (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs)
247 1.1 christos {
248 1.1 christos return !(lhs == rhs);
249 1.1 christos }
250 1.1 christos
251 1.1 christos /* Create an array view from a pointer to an array and an element
252 1.1 christos count.
253 1.1 christos
254 1.1 christos This is useful as alternative to constructing an array_view using
255 1.1 christos brace initialization when the size variable you have handy is of
256 1.1 christos signed type, since otherwise without an explicit cast the code
257 1.1 christos would be ill-formed.
258 1.1 christos
259 1.1 christos For example, with:
260 1.1 christos
261 1.1 christos extern void foo (int, int, gdb::array_view<value *>);
262 1.1 christos
263 1.1 christos value *args[2];
264 1.1 christos int nargs;
265 1.1 christos foo (1, 2, {values, nargs});
266 1.1 christos
267 1.1 christos You'd get:
268 1.1 christos
269 1.1 christos source.c:10: error: narrowing conversion of nargs from int to
270 1.1 christos size_t {aka long unsigned int} inside { } [-Werror=narrowing]
271 1.1 christos
272 1.1 christos You could fix it by writing the somewhat distracting explicit cast:
273 1.1 christos
274 1.1 christos foo (1, 2, {values, (size_t) nargs});
275 1.1 christos
276 1.1 christos Or by instantiating an array_view explicitly:
277 1.1 christos
278 1.1 christos foo (1, 2, gdb::array_view<value *>(values, nargs));
279 1.1 christos
280 1.1 christos Or, better, using make_array_view, which has the advantage of
281 1.1 christos inferring the arrav_view element's type:
282 1.1 christos
283 1.1 christos foo (1, 2, gdb::make_array_view (values, nargs));
284 1.1 christos */
285 1.1 christos
286 1.1 christos template<typename U>
287 1.1 christos constexpr inline array_view<U>
288 1.1 christos make_array_view (U *array, size_t size) noexcept
289 1.1 christos {
290 1.1 christos return {array, size};
291 1.1 christos }
292 1.1 christos
293 1.1 christos } /* namespace gdb */
294 1.1 christos
295 1.1 christos #endif
296