mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
d5f4488f09
Add some selftests for these two functions. To to make it easier to compare sequences of ranges, add operator== and operator!= to compare two gdb::array_view, and add operator== in struct range. gdb/ChangeLog: * value.c: Include "selftest.h" and "common/array-view.h". (struct range) <operator ==>: New. (test_ranges_contain): New. (check_ranges_vector): New. (test_insert_into_bit_range_vector): New. (_initialize_values): Register selftests. * common/array-view.h (operator==, operator!=): New.
207 lines
6.7 KiB
C++
207 lines
6.7 KiB
C++
/* Copyright (C) 2017-2018 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; }
|
|
|
|
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);
|
|
}
|
|
|
|
} /* namespace gdb */
|
|
|
|
#endif
|