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.1.4 christos #ifndef GDBSUPPORT_ARRAY_VIEW_H 19 1.1.1.4 christos #define GDBSUPPORT_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.1.4 christos using const_iterator = const T *; 92 1.1.1.4 christos using iterator = T *; 93 1.1 christos 94 1.1 christos /* Default construction creates an empty view. */ 95 1.1 christos constexpr array_view () noexcept 96 1.1 christos : m_array (nullptr), m_size (0) 97 1.1 christos {} 98 1.1 christos 99 1.1 christos /* Create an array view over a single object of the type of an 100 1.1 christos array_view element. The created view as size==1. This is 101 1.1 christos templated on U to allow constructing a array_view<const T> over a 102 1.1 christos (non-const) T. The "convertible" requirement makes sure that you 103 1.1 christos can't create an array_view<T> over a const T. */ 104 1.1 christos template<typename U, 105 1.1 christos typename = Requires<DecayedConvertible<U>>> 106 1.1 christos constexpr array_view (U &elem) noexcept 107 1.1 christos : m_array (&elem), m_size (1) 108 1.1 christos {} 109 1.1 christos 110 1.1 christos /* Same as above, for rvalue references. */ 111 1.1 christos template<typename U, 112 1.1 christos typename = Requires<DecayedConvertible<U>>> 113 1.1 christos constexpr array_view (U &&elem) noexcept 114 1.1 christos : m_array (&elem), m_size (1) 115 1.1 christos {} 116 1.1 christos 117 1.1 christos /* Create an array view from a pointer to an array and an element 118 1.1 christos count. */ 119 1.1 christos template<typename U, 120 1.1 christos typename = Requires<DecayedConvertible<U>>> 121 1.1 christos constexpr array_view (U *array, size_t size) noexcept 122 1.1 christos : m_array (array), m_size (size) 123 1.1 christos {} 124 1.1 christos 125 1.1 christos /* Create an array view from a range. This is templated on both U 126 1.1 christos an V to allow passing in a mix of 'const T *' and 'T *'. */ 127 1.1 christos template<typename U, typename V, 128 1.1 christos typename = Requires<DecayedConvertible<U>>, 129 1.1 christos typename = Requires<DecayedConvertible<V>>> 130 1.1 christos constexpr array_view (U *begin, V *end) noexcept 131 1.1 christos : m_array (begin), m_size (end - begin) 132 1.1 christos {} 133 1.1 christos 134 1.1 christos /* Create an array view from an array. */ 135 1.1 christos template<typename U, size_t Size, 136 1.1 christos typename = Requires<DecayedConvertible<U>>> 137 1.1 christos constexpr array_view (U (&array)[Size]) noexcept 138 1.1 christos : m_array (array), m_size (Size) 139 1.1 christos {} 140 1.1 christos 141 1.1 christos /* Create an array view from a contiguous container. E.g., 142 1.1 christos std::vector and std::array. */ 143 1.1 christos template<typename Container, 144 1.1 christos typename = Requires<gdb::Not<IsDecayedT<Container>>>, 145 1.1 christos typename 146 1.1.1.2 christos = Requires<DecayedConvertible 147 1.1.1.2 christos <typename std::remove_pointer 148 1.1.1.2 christos <decltype (std::declval<Container> ().data ()) 149 1.1.1.2 christos >::type>>, 150 1.1 christos typename 151 1.1 christos = Requires<std::is_convertible 152 1.1 christos <decltype (std::declval<Container> ().size ()), 153 1.1 christos size_type>>> 154 1.1 christos constexpr array_view (Container &&c) noexcept 155 1.1 christos : m_array (c.data ()), m_size (c.size ()) 156 1.1 christos {} 157 1.1 christos 158 1.1.1.3 christos /* Observer methods. */ 159 1.1.1.3 christos constexpr T *data () noexcept { return m_array; } 160 1.1 christos constexpr const T *data () const noexcept { return m_array; } 161 1.1 christos 162 1.1.1.4 christos constexpr iterator begin () const noexcept { return m_array; } 163 1.1.1.4 christos constexpr const_iterator cbegin () const noexcept { return m_array; } 164 1.1 christos 165 1.1.1.4 christos constexpr iterator end () const noexcept { return m_array + m_size; } 166 1.1.1.4 christos constexpr const_iterator cend () const noexcept { return m_array + m_size; } 167 1.1 christos 168 1.1.1.3 christos constexpr reference operator[] (size_t index) noexcept 169 1.1.1.2 christos { 170 1.1.1.2 christos #if defined(_GLIBCXX_DEBUG) 171 1.1.1.2 christos gdb_assert (index < m_size); 172 1.1.1.2 christos #endif 173 1.1.1.2 christos return m_array[index]; 174 1.1.1.2 christos } 175 1.1 christos constexpr const_reference operator[] (size_t index) const noexcept 176 1.1.1.2 christos { 177 1.1.1.3 christos #if defined(_GLIBCXX_DEBUG) 178 1.1.1.2 christos gdb_assert (index < m_size); 179 1.1.1.2 christos #endif 180 1.1.1.2 christos return m_array[index]; 181 1.1.1.2 christos } 182 1.1 christos 183 1.1 christos constexpr size_type size () const noexcept { return m_size; } 184 1.1 christos constexpr bool empty () const noexcept { return m_size == 0; } 185 1.1 christos 186 1.1 christos /* Slice an array view. */ 187 1.1 christos 188 1.1 christos /* Return a new array view over SIZE elements starting at START. */ 189 1.1.1.3 christos [[nodiscard]] 190 1.1 christos constexpr array_view<T> slice (size_type start, size_type size) const noexcept 191 1.1.1.2 christos { 192 1.1.1.3 christos #if defined(_GLIBCXX_DEBUG) 193 1.1.1.2 christos gdb_assert (start + size <= m_size); 194 1.1.1.2 christos #endif 195 1.1.1.2 christos return {m_array + start, size}; 196 1.1.1.2 christos } 197 1.1 christos 198 1.1 christos /* Return a new array view over all the elements after START, 199 1.1 christos inclusive. */ 200 1.1.1.3 christos [[nodiscard]] 201 1.1 christos constexpr array_view<T> slice (size_type start) const noexcept 202 1.1.1.2 christos { 203 1.1.1.3 christos #if defined(_GLIBCXX_DEBUG) 204 1.1.1.2 christos gdb_assert (start <= m_size); 205 1.1.1.2 christos #endif 206 1.1.1.2 christos return {m_array + start, size () - start}; 207 1.1.1.2 christos } 208 1.1 christos 209 1.1 christos private: 210 1.1 christos T *m_array; 211 1.1 christos size_type m_size; 212 1.1 christos }; 213 1.1 christos 214 1.1.1.2 christos /* Copy the contents referenced by the array view SRC to the array view DEST. 215 1.1.1.2 christos 216 1.1.1.2 christos The two array views must have the same length. */ 217 1.1.1.2 christos 218 1.1.1.2 christos template <typename U, typename T> 219 1.1.1.2 christos void copy (gdb::array_view<U> src, gdb::array_view<T> dest) 220 1.1.1.2 christos { 221 1.1.1.2 christos gdb_assert (dest.size () == src.size ()); 222 1.1.1.2 christos if (dest.data () < src.data ()) 223 1.1.1.2 christos std::copy (src.begin (), src.end (), dest.begin ()); 224 1.1.1.2 christos else if (dest.data () > src.data ()) 225 1.1.1.2 christos std::copy_backward (src.begin (), src.end (), dest.end ()); 226 1.1.1.2 christos } 227 1.1.1.2 christos 228 1.1 christos /* Compare LHS and RHS for (deep) equality. That is, whether LHS and 229 1.1 christos RHS have the same sizes, and whether each pair of elements of LHS 230 1.1 christos and RHS at the same position compares equal. */ 231 1.1 christos 232 1.1 christos template <typename T> 233 1.1 christos bool 234 1.1 christos operator== (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) 235 1.1 christos { 236 1.1 christos if (lhs.size () != rhs.size ()) 237 1.1 christos return false; 238 1.1 christos 239 1.1 christos for (size_t i = 0; i < lhs.size (); i++) 240 1.1 christos if (!(lhs[i] == rhs[i])) 241 1.1 christos return false; 242 1.1 christos 243 1.1 christos return true; 244 1.1 christos } 245 1.1 christos 246 1.1 christos /* Compare two array_views for inequality. */ 247 1.1 christos 248 1.1 christos template <typename T> 249 1.1 christos bool 250 1.1 christos operator!= (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) 251 1.1 christos { 252 1.1 christos return !(lhs == rhs); 253 1.1 christos } 254 1.1 christos 255 1.1 christos /* Create an array view from a pointer to an array and an element 256 1.1 christos count. 257 1.1 christos 258 1.1 christos This is useful as alternative to constructing an array_view using 259 1.1 christos brace initialization when the size variable you have handy is of 260 1.1 christos signed type, since otherwise without an explicit cast the code 261 1.1 christos would be ill-formed. 262 1.1 christos 263 1.1 christos For example, with: 264 1.1 christos 265 1.1 christos extern void foo (int, int, gdb::array_view<value *>); 266 1.1 christos 267 1.1 christos value *args[2]; 268 1.1 christos int nargs; 269 1.1 christos foo (1, 2, {values, nargs}); 270 1.1 christos 271 1.1 christos You'd get: 272 1.1 christos 273 1.1 christos source.c:10: error: narrowing conversion of nargs from int to 274 1.1 christos size_t {aka long unsigned int} inside { } [-Werror=narrowing] 275 1.1 christos 276 1.1 christos You could fix it by writing the somewhat distracting explicit cast: 277 1.1 christos 278 1.1 christos foo (1, 2, {values, (size_t) nargs}); 279 1.1 christos 280 1.1 christos Or by instantiating an array_view explicitly: 281 1.1 christos 282 1.1 christos foo (1, 2, gdb::array_view<value *>(values, nargs)); 283 1.1 christos 284 1.1 christos Or, better, using make_array_view, which has the advantage of 285 1.1.1.3 christos inferring the array_view element's type: 286 1.1 christos 287 1.1 christos foo (1, 2, gdb::make_array_view (values, nargs)); 288 1.1 christos */ 289 1.1 christos 290 1.1 christos template<typename U> 291 1.1 christos constexpr inline array_view<U> 292 1.1 christos make_array_view (U *array, size_t size) noexcept 293 1.1 christos { 294 1.1 christos return {array, size}; 295 1.1 christos } 296 1.1 christos 297 1.1 christos } /* namespace gdb */ 298 1.1 christos 299 1.1.1.4 christos #endif /* GDBSUPPORT_ARRAY_VIEW_H */ 300