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