diff options
Diffstat (limited to 'gdbsupport/array-view.h')
-rw-r--r-- | gdbsupport/array-view.h | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/gdbsupport/array-view.h b/gdbsupport/array-view.h new file mode 100644 index 0000000..ed5edc7 --- /dev/null +++ b/gdbsupport/array-view.h @@ -0,0 +1,259 @@ +/* Copyright (C) 2017-2020 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef COMMON_ARRAY_VIEW_H +#define COMMON_ARRAY_VIEW_H + +#include "traits.h" +#include <type_traits> + +/* An array_view is an abstraction that provides a non-owning view + over a sequence of contiguous objects. + + A way to put it is that array_view is to std::vector (and + std::array and built-in arrays with rank==1) like std::string_view + is to std::string. + + The main intent of array_view is to use it as function input + parameter type, making it possible to pass in any sequence of + contiguous objects, irrespective of whether the objects live on the + stack or heap and what actual container owns them. Implicit + construction from the element type is supported too, making it easy + to call functions that expect an array of elements when you only + have one element (usually on the stack). For example: + + struct A { .... }; + void function (gdb::array_view<A> as); + + std::vector<A> std_vec = ...; + std::array<A, N> std_array = ...; + A array[] = {...}; + A elem; + + function (std_vec); + function (std_array); + function (array); + function (elem); + + Views can be either mutable or const. A const view is simply + created by specifying a const T as array_view template parameter, + in which case operator[] of non-const array_view objects ends up + returning const references. Making the array_view itself const is + analogous to making a pointer itself be const. I.e., disables + re-seating the view/pointer. + + Since array_view objects are small (pointer plus size), and + designed to be trivially copyable, they should generally be passed + around by value. + + You can find unit tests covering the whole API in + unittests/array-view-selftests.c. */ + +namespace gdb { + +template <typename T> +class array_view +{ + /* True iff decayed T is the same as decayed U. E.g., we want to + say that 'T&' is the same as 'const T'. */ + template <typename U> + using IsDecayedT = typename std::is_same<typename std::decay<T>::type, + typename std::decay<U>::type>; + + /* True iff decayed T is the same as decayed U, and 'U *' is + implicitly convertible to 'T *'. This is a requirement for + several methods. */ + template <typename U> + using DecayedConvertible = gdb::And<IsDecayedT<U>, + std::is_convertible<U *, T *>>; + +public: + using value_type = T; + using reference = T &; + using const_reference = const T &; + using size_type = size_t; + + /* Default construction creates an empty view. */ + constexpr array_view () noexcept + : m_array (nullptr), m_size (0) + {} + + /* Create an array view over a single object of the type of an + array_view element. The created view as size==1. This is + templated on U to allow constructing a array_view<const T> over a + (non-const) T. The "convertible" requirement makes sure that you + can't create an array_view<T> over a const T. */ + template<typename U, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U &elem) noexcept + : m_array (&elem), m_size (1) + {} + + /* Same as above, for rvalue references. */ + template<typename U, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U &&elem) noexcept + : m_array (&elem), m_size (1) + {} + + /* Create an array view from a pointer to an array and an element + count. */ + template<typename U, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U *array, size_t size) noexcept + : m_array (array), m_size (size) + {} + + /* Create an array view from a range. This is templated on both U + an V to allow passing in a mix of 'const T *' and 'T *'. */ + template<typename U, typename V, + typename = Requires<DecayedConvertible<U>>, + typename = Requires<DecayedConvertible<V>>> + constexpr array_view (U *begin, V *end) noexcept + : m_array (begin), m_size (end - begin) + {} + + /* Create an array view from an array. */ + template<typename U, size_t Size, + typename = Requires<DecayedConvertible<U>>> + constexpr array_view (U (&array)[Size]) noexcept + : m_array (array), m_size (Size) + {} + + /* Create an array view from a contiguous container. E.g., + std::vector and std::array. */ + template<typename Container, + typename = Requires<gdb::Not<IsDecayedT<Container>>>, + typename + = Requires<std::is_convertible + <decltype (std::declval<Container> ().data ()), + T *>>, + typename + = Requires<std::is_convertible + <decltype (std::declval<Container> ().size ()), + size_type>>> + constexpr array_view (Container &&c) noexcept + : m_array (c.data ()), m_size (c.size ()) + {} + + /* Observer methods. Some of these can't be constexpr until we + require C++14. */ + /*constexpr14*/ T *data () noexcept { return m_array; } + constexpr const T *data () const noexcept { return m_array; } + + /*constexpr14*/ T *begin () noexcept { return m_array; } + constexpr const T *begin () const noexcept { return m_array; } + + /*constexpr14*/ T *end () noexcept { return m_array + m_size; } + constexpr const T *end () const noexcept { return m_array + m_size; } + + /*constexpr14*/ reference operator[] (size_t index) noexcept + { return m_array[index]; } + constexpr const_reference operator[] (size_t index) const noexcept + { return m_array[index]; } + + constexpr size_type size () const noexcept { return m_size; } + constexpr bool empty () const noexcept { return m_size == 0; } + + /* Slice an array view. */ + + /* Return a new array view over SIZE elements starting at START. */ + constexpr array_view<T> slice (size_type start, size_type size) const noexcept + { return {m_array + start, size}; } + + /* Return a new array view over all the elements after START, + inclusive. */ + constexpr array_view<T> slice (size_type start) const noexcept + { return {m_array + start, size () - start}; } + +private: + T *m_array; + size_type m_size; +}; + +/* Compare LHS and RHS for (deep) equality. That is, whether LHS and + RHS have the same sizes, and whether each pair of elements of LHS + and RHS at the same position compares equal. */ + +template <typename T> +bool +operator== (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) +{ + if (lhs.size () != rhs.size ()) + return false; + + for (size_t i = 0; i < lhs.size (); i++) + if (!(lhs[i] == rhs[i])) + return false; + + return true; +} + +/* Compare two array_views for inequality. */ + +template <typename T> +bool +operator!= (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) +{ + return !(lhs == rhs); +} + +/* Create an array view from a pointer to an array and an element + count. + + This is useful as alternative to constructing an array_view using + brace initialization when the size variable you have handy is of + signed type, since otherwise without an explicit cast the code + would be ill-formed. + + For example, with: + + extern void foo (int, int, gdb::array_view<value *>); + + value *args[2]; + int nargs; + foo (1, 2, {values, nargs}); + + You'd get: + + source.c:10: error: narrowing conversion of ‘nargs’ from ‘int’ to + ‘size_t {aka long unsigned int}’ inside { } [-Werror=narrowing] + + You could fix it by writing the somewhat distracting explicit cast: + + foo (1, 2, {values, (size_t) nargs}); + + Or by instantiating an array_view explicitly: + + foo (1, 2, gdb::array_view<value *>(values, nargs)); + + Or, better, using make_array_view, which has the advantage of + inferring the arrav_view element's type: + + foo (1, 2, gdb::make_array_view (values, nargs)); +*/ + +template<typename U> +constexpr inline array_view<U> +make_array_view (U *array, size_t size) noexcept +{ + return {array, size}; +} + +} /* namespace gdb */ + +#endif |