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