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