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