mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-10 01:23:17 +00:00
I think this overload will be useful for the following reasons.
Consider a templated function like this:
template <typename T>
void func(gdb::array_view<T> view) {}
Trying to pass an array to this function doesn't work, as template
argument deduction fails:
test.c:698:8: error: no matching function for call to ‘func(int [12])’
698 | func (array);
| ~~~~~^~~~~~~
test.c:686:6: note: candidate: ‘template<class T> void func(gdb::array_view<U>)’
686 | void func(gdb::array_view<T> view) {}
| ^~~~
test.c:686:6: note: template argument deduction/substitution failed:
test.c:698:8: note: mismatched types ‘gdb::array_view<U>’ and ‘int*’
698 | func (array);
| ~~~~~^~~~~~~
Similarly, trying to compare a view with an array doesn't work. This:
int array[12];
gdb::array_view<int> view;
if (view == array) {}
... fails with:
test.c:698:8: error: no matching function for call to ‘func(int [12])’
698 | func (array);
| ~~~~~^~~~~~~
test.c:686:6: note: candidate: ‘template<class T> void func(gdb::array_view<U>)’
686 | void func(gdb::array_view<T> view) {}
| ^~~~
test.c:686:6: note: template argument deduction/substitution failed:
test.c:698:8: note: mismatched types ‘gdb::array_view<U>’ and ‘int*’
698 | func (array);
| ~~~~~^~~~~~~
With this new overload, we can do:
func (gdb::make_array_view (array));
and
if (view == gdb::make_array_view (array)) {}
This is not ideal, I wish that omitting `gdb::make_array_view` would
just work, but at least it allows creating an array view and have the
element type automatically deduced from the array type.
If someone knows how to make these cases "just work", I would be happy
to know how.
Change-Id: I6a71919d2d5a385e6826801d53f5071b470fef5f
Approved-By: Tom Tromey <tom@tromey.com>
707 lines
18 KiB
C
707 lines
18 KiB
C
/* Self tests for array_view for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 2017-2024 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/>. */
|
|
|
|
#include "gdbsupport/selftest.h"
|
|
#include "gdbsupport/array-view.h"
|
|
#include <array>
|
|
#include <vector>
|
|
|
|
namespace selftests {
|
|
namespace array_view_tests {
|
|
|
|
/* Triviality checks. */
|
|
#define CHECK_TRAIT(TRAIT) \
|
|
static_assert (std::TRAIT<gdb::array_view<gdb_byte>>::value, "")
|
|
|
|
CHECK_TRAIT (is_trivially_copyable);
|
|
CHECK_TRAIT (is_trivially_move_assignable);
|
|
CHECK_TRAIT (is_trivially_move_constructible);
|
|
CHECK_TRAIT (is_trivially_destructible);
|
|
|
|
#undef CHECK_TRAIT
|
|
|
|
/* Wrapper around std::is_convertible to make the code using it a bit
|
|
shorter. (With C++14 we'd use a variable template instead.) */
|
|
|
|
template<typename From, typename To>
|
|
static constexpr bool
|
|
is_convertible ()
|
|
{
|
|
return std::is_convertible<From, To>::value;
|
|
}
|
|
|
|
/* Check for implicit conversion to immutable and mutable views. */
|
|
|
|
static constexpr bool
|
|
check_convertible ()
|
|
{
|
|
using T = gdb_byte;
|
|
using gdb::array_view;
|
|
|
|
return (true
|
|
/* immutable array_view */
|
|
&& is_convertible<const T (&) [1], array_view<const T>> ()
|
|
&& is_convertible<T (&) [1], array_view<const T>> ()
|
|
&& is_convertible<const T, array_view<const T>> ()
|
|
&& is_convertible<T, array_view<const T>> ()
|
|
|
|
/* mutable array_view */
|
|
&& is_convertible<T (&) [1], array_view<T>> ()
|
|
&& !is_convertible<const T (&) [1], array_view<T>> ()
|
|
&& is_convertible<T, array_view<T>> ()
|
|
&& !is_convertible<const T, array_view<T>> ()
|
|
|
|
/* While float is implicitly convertible to gdb_byte, we
|
|
don't want implicit float->array_view<gdb_byte>
|
|
conversion. */
|
|
&& !is_convertible<float, array_view<const T>> ()
|
|
&& !is_convertible<float, array_view<T>> ());
|
|
}
|
|
|
|
static_assert (check_convertible (), "");
|
|
|
|
namespace no_slicing
|
|
{
|
|
struct A { int i; };
|
|
struct B : A { int j; };
|
|
struct C : A { int l; };
|
|
|
|
/* Check that there's no array->view conversion for arrays of derived types or
|
|
subclasses. */
|
|
static constexpr bool
|
|
check ()
|
|
{
|
|
using gdb::array_view;
|
|
|
|
return (true
|
|
|
|
/* array->view */
|
|
|
|
&& is_convertible <A (&)[1], array_view<A>> ()
|
|
&& !is_convertible <B (&)[1], array_view<A>> ()
|
|
&& !is_convertible <C (&)[1], array_view<A>> ()
|
|
|
|
&& !is_convertible <A (&)[1], array_view<B>> ()
|
|
&& is_convertible <B (&)[1], array_view<B>> ()
|
|
&& !is_convertible <C (&)[1], array_view<B>> ()
|
|
|
|
/* elem->view */
|
|
|
|
&& is_convertible <A, array_view<A>> ()
|
|
&& !is_convertible <B, array_view<A>> ()
|
|
&& !is_convertible <C, array_view<A>> ()
|
|
|
|
&& !is_convertible <A, array_view<B>> ()
|
|
&& is_convertible <B, array_view<B>> ()
|
|
&& !is_convertible <C, array_view<B>> ());
|
|
}
|
|
|
|
/* Check that there's no container->view conversion for containers of derived
|
|
types or subclasses. */
|
|
|
|
template<template<typename ...> class Container>
|
|
static constexpr bool
|
|
check_ctor_from_container ()
|
|
{
|
|
using gdb::array_view;
|
|
|
|
return ( is_convertible <Container<A>, array_view<A>> ()
|
|
&& !is_convertible <Container<B>, array_view<A>> ()
|
|
&& !is_convertible <Container<C>, array_view<A>> ()
|
|
|
|
&& !is_convertible <Container<A>, array_view<B>> ()
|
|
&& is_convertible <Container<B>, array_view<B>> ()
|
|
&& !is_convertible <Container<C>, array_view<B>> ());
|
|
}
|
|
|
|
} /* namespace no_slicing */
|
|
|
|
/* std::array with only one template argument, so we can pass it to
|
|
check_ctor_from_container. */
|
|
template<typename T> using StdArray1 = std::array<T, 1>;
|
|
|
|
static_assert (no_slicing::check (), "");
|
|
static_assert (no_slicing::check_ctor_from_container<std::vector> (), "");
|
|
static_assert (no_slicing::check_ctor_from_container<StdArray1> (), "");
|
|
static_assert (no_slicing::check_ctor_from_container<gdb::array_view> (), "");
|
|
|
|
/* Check that array_view implicitly converts from std::vector. */
|
|
|
|
static constexpr bool
|
|
check_convertible_from_std_vector ()
|
|
{
|
|
using gdb::array_view;
|
|
using T = gdb_byte;
|
|
|
|
/* Note there's no such thing as std::vector<const T>. */
|
|
|
|
return (true
|
|
&& is_convertible <std::vector<T>, array_view<T>> ()
|
|
&& is_convertible <std::vector<T>, array_view<const T>> ());
|
|
}
|
|
|
|
static_assert (check_convertible_from_std_vector (), "");
|
|
|
|
/* Check that array_view implicitly converts from std::array. */
|
|
|
|
static constexpr bool
|
|
check_convertible_from_std_array ()
|
|
{
|
|
using gdb::array_view;
|
|
using T = gdb_byte;
|
|
|
|
/* Note: a non-const T view can't refer to a const T array. */
|
|
|
|
return (true
|
|
&& is_convertible <std::array<T, 1>, array_view<T>> ()
|
|
&& is_convertible <std::array<T, 1>, array_view<const T>> ()
|
|
&& !is_convertible <std::array<const T, 1>, array_view<T>> ()
|
|
&& is_convertible <std::array<const T, 1>, array_view<const T>> ());
|
|
}
|
|
|
|
static_assert (check_convertible_from_std_array (), "");
|
|
|
|
/* Check that VIEW views C (a container like std::vector/std::array)
|
|
correctly. */
|
|
|
|
template<typename View, typename Container>
|
|
static bool
|
|
check_container_view (const View &view, const Container &c)
|
|
{
|
|
if (view.empty ())
|
|
return false;
|
|
if (view.size () != c.size ())
|
|
return false;
|
|
if (view.data () != c.data ())
|
|
return false;
|
|
for (size_t i = 0; i < c.size (); i++)
|
|
{
|
|
if (&view[i] != &c[i])
|
|
return false;
|
|
if (view[i] != c[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Check that VIEW views E (an object of the type of a view element)
|
|
correctly. */
|
|
|
|
template<typename View, typename Elem>
|
|
static bool
|
|
check_elem_view (const View &view, const Elem &e)
|
|
{
|
|
if (view.empty ())
|
|
return false;
|
|
if (view.size () != 1)
|
|
return false;
|
|
if (view.data () != &e)
|
|
return false;
|
|
if (&view[0] != &e)
|
|
return false;
|
|
if (view[0] != e)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/* Check for operator[]. The first overload is taken iff
|
|
'view<T>()[0] = T()' is a valid expression. */
|
|
|
|
template<typename View,
|
|
typename = decltype (std::declval<View> ()[0]
|
|
= std::declval<typename View::value_type> ())>
|
|
static bool
|
|
check_op_subscript (const View &view)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* This overload is taken iff 'view<T>()[0] = T()' is not a valid
|
|
expression. */
|
|
|
|
static bool
|
|
check_op_subscript (...)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* Check construction with pointer + size. This is a template in
|
|
order to test both gdb_byte and const gdb_byte. */
|
|
|
|
template<typename T>
|
|
static void
|
|
check_ptr_size_ctor ()
|
|
{
|
|
T data[] = {0x11, 0x22, 0x33, 0x44};
|
|
|
|
gdb::array_view<T> view (data + 1, 2);
|
|
|
|
SELF_CHECK (!view.empty ());
|
|
SELF_CHECK (view.size () == 2);
|
|
SELF_CHECK (view.data () == &data[1]);
|
|
SELF_CHECK (view[0] == data[1]);
|
|
SELF_CHECK (view[1] == data[2]);
|
|
|
|
gdb::array_view<const T> cview (data + 1, 2);
|
|
SELF_CHECK (!cview.empty ());
|
|
SELF_CHECK (cview.size () == 2);
|
|
SELF_CHECK (cview.data () == &data[1]);
|
|
SELF_CHECK (cview[0] == data[1]);
|
|
SELF_CHECK (cview[1] == data[2]);
|
|
}
|
|
|
|
/* Asserts std::is_constructible. */
|
|
|
|
template<typename T, typename... Args>
|
|
static constexpr bool
|
|
require_not_constructible ()
|
|
{
|
|
static_assert (!std::is_constructible<T, Args...>::value, "");
|
|
|
|
/* constexpr functions can't return void in C++11 (N3444). */
|
|
return true;
|
|
};
|
|
|
|
/* Check the array_view<T>(PTR, SIZE) ctor, when T is a pointer. */
|
|
|
|
static void
|
|
check_ptr_size_ctor2 ()
|
|
{
|
|
struct A {};
|
|
A an_a;
|
|
|
|
A *array[] = { &an_a };
|
|
const A * const carray[] = { &an_a };
|
|
|
|
gdb::array_view<A *> v1 = {array, ARRAY_SIZE (array)};
|
|
gdb::array_view<A *> v2 = {array, (char) ARRAY_SIZE (array)};
|
|
gdb::array_view<A * const> v3 = {array, ARRAY_SIZE (array)};
|
|
gdb::array_view<const A * const> cv1 = {carray, ARRAY_SIZE (carray)};
|
|
|
|
require_not_constructible<gdb::array_view<A *>, decltype (carray), size_t> ();
|
|
|
|
SELF_CHECK (v1[0] == array[0]);
|
|
SELF_CHECK (v2[0] == array[0]);
|
|
SELF_CHECK (v3[0] == array[0]);
|
|
|
|
SELF_CHECK (!v1.empty ());
|
|
SELF_CHECK (v1.size () == 1);
|
|
SELF_CHECK (v1.data () == &array[0]);
|
|
|
|
SELF_CHECK (cv1[0] == carray[0]);
|
|
|
|
SELF_CHECK (!cv1.empty ());
|
|
SELF_CHECK (cv1.size () == 1);
|
|
SELF_CHECK (cv1.data () == &carray[0]);
|
|
}
|
|
|
|
/* Check construction with a pair of pointers. This is a template in
|
|
order to test both gdb_byte and const gdb_byte. */
|
|
|
|
template<typename T>
|
|
static void
|
|
check_ptr_ptr_ctor ()
|
|
{
|
|
T data[] = {0x11, 0x22, 0x33, 0x44};
|
|
|
|
gdb::array_view<T> view (data + 1, data + 3);
|
|
|
|
SELF_CHECK (!view.empty ());
|
|
SELF_CHECK (view.size () == 2);
|
|
SELF_CHECK (view.data () == &data[1]);
|
|
SELF_CHECK (view[0] == data[1]);
|
|
SELF_CHECK (view[1] == data[2]);
|
|
|
|
gdb_byte array[] = {0x11, 0x22, 0x33, 0x44};
|
|
const gdb_byte *p1 = array;
|
|
gdb_byte *p2 = array + ARRAY_SIZE (array);
|
|
gdb::array_view<const gdb_byte> view2 (p1, p2);
|
|
}
|
|
|
|
/* Check construction with a pair of pointers of mixed constness. */
|
|
|
|
static void
|
|
check_ptr_ptr_mixed_cv ()
|
|
{
|
|
gdb_byte array[] = {0x11, 0x22, 0x33, 0x44};
|
|
const gdb_byte *cp = array;
|
|
gdb_byte *p = array;
|
|
gdb::array_view<const gdb_byte> view1 (cp, p);
|
|
gdb::array_view<const gdb_byte> view2 (p, cp);
|
|
SELF_CHECK (view1.empty ());
|
|
SELF_CHECK (view2.empty ());
|
|
}
|
|
|
|
/* Check range-for support (i.e., begin()/end()). This is a template
|
|
in order to test both gdb_byte and const gdb_byte. */
|
|
|
|
template<typename T>
|
|
static void
|
|
check_range_for ()
|
|
{
|
|
T data[] = {1, 2, 3, 4};
|
|
gdb::array_view<T> view (data);
|
|
|
|
typename std::decay<T>::type sum = 0;
|
|
for (auto &elem : view)
|
|
sum += elem;
|
|
SELF_CHECK (sum == 1 + 2 + 3 + 4);
|
|
}
|
|
|
|
template<typename T>
|
|
static void
|
|
check_iterator ()
|
|
{
|
|
T data[] = {1, 2, 3, 4};
|
|
gdb::array_view<T> view (data);
|
|
|
|
typename std::decay<T>::type sum = 0;
|
|
for (typename gdb::array_view<T>::iterator it = view.begin ();
|
|
it != view.end (); it++)
|
|
{
|
|
*it *= 2;
|
|
sum += *it;
|
|
}
|
|
|
|
SELF_CHECK (sum == 2 + 4 + 6 + 8);
|
|
}
|
|
|
|
template<typename T>
|
|
static void
|
|
check_const_iterator ()
|
|
{
|
|
T data[] = {1, 2, 3, 4};
|
|
gdb::array_view<T> view (data);
|
|
|
|
typename std::decay<T>::type sum = 0;
|
|
for (typename gdb::array_view<T>::const_iterator it = view.cbegin ();
|
|
it != view.cend (); it++)
|
|
sum += *it;
|
|
|
|
SELF_CHECK (sum == 1 + 2 + 3 + 4);
|
|
}
|
|
|
|
/* Entry point. */
|
|
|
|
static void
|
|
run_tests ()
|
|
{
|
|
/* Empty views. */
|
|
{
|
|
constexpr gdb::array_view<gdb_byte> view1;
|
|
constexpr gdb::array_view<const gdb_byte> view2;
|
|
|
|
static_assert (view1.empty (), "");
|
|
static_assert (view1.data () == nullptr, "");
|
|
static_assert (view1.size () == 0, "");
|
|
static_assert (view2.empty (), "");
|
|
static_assert (view2.size () == 0, "");
|
|
static_assert (view2.data () == nullptr, "");
|
|
}
|
|
|
|
std::vector<gdb_byte> vec = {0x11, 0x22, 0x33, 0x44 };
|
|
std::array<gdb_byte, 4> array = {{0x11, 0x22, 0x33, 0x44}};
|
|
|
|
/* Various tests of views over std::vector. */
|
|
{
|
|
gdb::array_view<gdb_byte> view = vec;
|
|
SELF_CHECK (check_container_view (view, vec));
|
|
gdb::array_view<const gdb_byte> cview = vec;
|
|
SELF_CHECK (check_container_view (cview, vec));
|
|
}
|
|
|
|
/* Likewise, over std::array. */
|
|
{
|
|
gdb::array_view<gdb_byte> view = array;
|
|
SELF_CHECK (check_container_view (view, array));
|
|
gdb::array_view<gdb_byte> cview = array;
|
|
SELF_CHECK (check_container_view (cview, array));
|
|
}
|
|
|
|
/* op=(std::vector/std::array/elem) */
|
|
{
|
|
gdb::array_view<gdb_byte> view;
|
|
|
|
view = vec;
|
|
SELF_CHECK (check_container_view (view, vec));
|
|
view = std::move (vec);
|
|
SELF_CHECK (check_container_view (view, vec));
|
|
|
|
view = array;
|
|
SELF_CHECK (check_container_view (view, array));
|
|
view = std::move (array);
|
|
SELF_CHECK (check_container_view (view, array));
|
|
|
|
gdb_byte elem = 0;
|
|
view = elem;
|
|
SELF_CHECK (check_elem_view (view, elem));
|
|
view = std::move (elem);
|
|
SELF_CHECK (check_elem_view (view, elem));
|
|
}
|
|
|
|
/* Test copy/move ctor and mutable->immutable conversion. */
|
|
{
|
|
gdb_byte data[] = {0x11, 0x22, 0x33, 0x44};
|
|
gdb::array_view<gdb_byte> view1 = data;
|
|
gdb::array_view<gdb_byte> view2 = view1;
|
|
gdb::array_view<gdb_byte> view3 = std::move (view1);
|
|
gdb::array_view<const gdb_byte> cview1 = data;
|
|
gdb::array_view<const gdb_byte> cview2 = cview1;
|
|
gdb::array_view<const gdb_byte> cview3 = std::move (cview1);
|
|
SELF_CHECK (view1[0] == data[0]);
|
|
SELF_CHECK (view2[0] == data[0]);
|
|
SELF_CHECK (view3[0] == data[0]);
|
|
SELF_CHECK (cview1[0] == data[0]);
|
|
SELF_CHECK (cview2[0] == data[0]);
|
|
SELF_CHECK (cview3[0] == data[0]);
|
|
}
|
|
|
|
/* Same, but op=(view). */
|
|
{
|
|
gdb_byte data[] = {0x55, 0x66, 0x77, 0x88};
|
|
gdb::array_view<gdb_byte> view1;
|
|
gdb::array_view<gdb_byte> view2;
|
|
gdb::array_view<gdb_byte> view3;
|
|
gdb::array_view<const gdb_byte> cview1;
|
|
gdb::array_view<const gdb_byte> cview2;
|
|
gdb::array_view<const gdb_byte> cview3;
|
|
|
|
view1 = data;
|
|
view2 = view1;
|
|
view3 = std::move (view1);
|
|
cview1 = data;
|
|
cview2 = cview1;
|
|
cview3 = std::move (cview1);
|
|
SELF_CHECK (view1[0] == data[0]);
|
|
SELF_CHECK (view2[0] == data[0]);
|
|
SELF_CHECK (view3[0] == data[0]);
|
|
SELF_CHECK (cview1[0] == data[0]);
|
|
SELF_CHECK (cview2[0] == data[0]);
|
|
SELF_CHECK (cview3[0] == data[0]);
|
|
}
|
|
|
|
/* op[] */
|
|
{
|
|
std::vector<gdb_byte> vec2 = {0x11, 0x22};
|
|
gdb::array_view<gdb_byte> view = vec2;
|
|
gdb::array_view<const gdb_byte> cview = vec2;
|
|
|
|
/* Check that op[] on a non-const view of non-const T returns a
|
|
mutable reference. */
|
|
view[0] = 0x33;
|
|
SELF_CHECK (vec2[0] == 0x33);
|
|
|
|
/* OTOH, check that assigning through op[] on a view of const T
|
|
wouldn't compile. */
|
|
SELF_CHECK (!check_op_subscript (cview));
|
|
/* For completeness. */
|
|
SELF_CHECK (check_op_subscript (view));
|
|
}
|
|
|
|
check_ptr_size_ctor<const gdb_byte> ();
|
|
check_ptr_size_ctor<gdb_byte> ();
|
|
check_ptr_size_ctor2 ();
|
|
check_ptr_ptr_ctor<const gdb_byte> ();
|
|
check_ptr_ptr_ctor<gdb_byte> ();
|
|
check_ptr_ptr_mixed_cv ();
|
|
|
|
check_range_for<gdb_byte> ();
|
|
check_range_for<const gdb_byte> ();
|
|
check_iterator<gdb_byte> ();
|
|
check_const_iterator<gdb_byte> ();
|
|
check_const_iterator<const gdb_byte> ();
|
|
|
|
/* Check that the right ctor overloads are taken when the element is
|
|
a container. */
|
|
{
|
|
using Vec = std::vector<gdb_byte>;
|
|
Vec vecs[3];
|
|
|
|
gdb::array_view<Vec> view_array = vecs;
|
|
SELF_CHECK (view_array.size () == 3);
|
|
|
|
Vec elem;
|
|
gdb::array_view<Vec> view_elem = elem;
|
|
SELF_CHECK (view_elem.size () == 1);
|
|
}
|
|
|
|
/* gdb::make_array_view, int length. */
|
|
{
|
|
gdb_byte data[] = {0x55, 0x66, 0x77, 0x88};
|
|
int len = sizeof (data) / sizeof (data[0]);
|
|
auto view = gdb::make_array_view (data, len);
|
|
|
|
SELF_CHECK (view.data () == data);
|
|
SELF_CHECK (view.size () == len);
|
|
|
|
for (size_t i = 0; i < len; i++)
|
|
SELF_CHECK (view[i] == data[i]);
|
|
}
|
|
|
|
/* gdb::make_array_view with an array. */
|
|
{
|
|
const gdb_byte data[] = {0x55, 0x66, 0x77, 0x88};
|
|
const auto len = sizeof (data) / sizeof (data[0]);
|
|
const auto view = gdb::make_array_view (data);
|
|
|
|
SELF_CHECK (view.data () == data);
|
|
SELF_CHECK (view.size () == len);
|
|
|
|
for (std::size_t i = 0; i < len; ++i)
|
|
SELF_CHECK (view[i] == data[i]);
|
|
}
|
|
|
|
/* Test slicing. */
|
|
{
|
|
gdb_byte data[] = {0x55, 0x66, 0x77, 0x88, 0x99};
|
|
gdb::array_view<gdb_byte> view = data;
|
|
|
|
{
|
|
auto slc = view.slice (1, 3);
|
|
SELF_CHECK (slc.data () == data + 1);
|
|
SELF_CHECK (slc.size () == 3);
|
|
SELF_CHECK (slc[0] == data[1]);
|
|
SELF_CHECK (slc[0] == view[1]);
|
|
}
|
|
|
|
{
|
|
auto slc = view.slice (2);
|
|
SELF_CHECK (slc.data () == data + 2);
|
|
SELF_CHECK (slc.size () == 3);
|
|
SELF_CHECK (slc[0] == view[2]);
|
|
SELF_CHECK (slc[0] == data[2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void
|
|
run_copy_test ()
|
|
{
|
|
/* Test non-overlapping copy. */
|
|
{
|
|
const std::vector<T> src_v = {1, 2, 3, 4};
|
|
std::vector<T> dest_v (4, -1);
|
|
|
|
SELF_CHECK (dest_v != src_v);
|
|
copy (gdb::array_view<const T> (src_v), gdb::array_view<T> (dest_v));
|
|
SELF_CHECK (dest_v == src_v);
|
|
}
|
|
|
|
/* Test overlapping copy, where the source is before the destination. */
|
|
{
|
|
std::vector<T> vec = {1, 2, 3, 4, 5, 6, 7, 8};
|
|
gdb::array_view<T> v = vec;
|
|
|
|
copy (v.slice (1, 4),
|
|
v.slice (2, 4));
|
|
|
|
std::vector<T> expected = {1, 2, 2, 3, 4, 5, 7, 8};
|
|
SELF_CHECK (vec == expected);
|
|
}
|
|
|
|
/* Test overlapping copy, where the source is after the destination. */
|
|
{
|
|
std::vector<T> vec = {1, 2, 3, 4, 5, 6, 7, 8};
|
|
gdb::array_view<T> v = vec;
|
|
|
|
copy (v.slice (2, 4),
|
|
v.slice (1, 4));
|
|
|
|
std::vector<T> expected = {1, 3, 4, 5, 6, 6, 7, 8};
|
|
SELF_CHECK (vec == expected);
|
|
}
|
|
|
|
/* Test overlapping copy, where the source is the same as the destination. */
|
|
{
|
|
std::vector<T> vec = {1, 2, 3, 4, 5, 6, 7, 8};
|
|
gdb::array_view<T> v = vec;
|
|
|
|
copy (v.slice (2, 4),
|
|
v.slice (2, 4));
|
|
|
|
std::vector<T> expected = {1, 2, 3, 4, 5, 6, 7, 8};
|
|
SELF_CHECK (vec == expected);
|
|
}
|
|
}
|
|
|
|
/* Class with a non-trivial copy assignment operator, used to test the
|
|
array_view copy function. */
|
|
struct foo
|
|
{
|
|
/* Can be implicitly constructed from an int, such that we can use the same
|
|
templated test function to test against array_view<int> and
|
|
array_view<foo>. */
|
|
foo (int n)
|
|
: n (n)
|
|
{}
|
|
|
|
/* Needed to avoid -Wdeprecated-copy-with-user-provided-copy error with
|
|
Clang. */
|
|
foo (const foo &other) = default;
|
|
|
|
void operator= (const foo &other)
|
|
{
|
|
this->n = other.n;
|
|
this->n_assign_op_called++;
|
|
}
|
|
|
|
bool operator==(const foo &other) const
|
|
{
|
|
return this->n == other.n;
|
|
}
|
|
|
|
int n;
|
|
|
|
/* Number of times the assignment operator has been called. */
|
|
static int n_assign_op_called;
|
|
};
|
|
|
|
int foo::n_assign_op_called = 0;
|
|
|
|
/* Test the array_view copy free function. */
|
|
|
|
static void
|
|
run_copy_tests ()
|
|
{
|
|
/* Test with a trivial type. */
|
|
run_copy_test<int> ();
|
|
|
|
/* Test with a non-trivial type. */
|
|
foo::n_assign_op_called = 0;
|
|
run_copy_test<foo> ();
|
|
|
|
/* Make sure that for the non-trivial type foo, the assignment operator was
|
|
called an amount of times that makes sense. */
|
|
SELF_CHECK (foo::n_assign_op_called == 12);
|
|
}
|
|
|
|
} /* namespace array_view_tests */
|
|
} /* namespace selftests */
|
|
|
|
void _initialize_array_view_selftests ();
|
|
void
|
|
_initialize_array_view_selftests ()
|
|
{
|
|
selftests::register_test ("array_view",
|
|
selftests::array_view_tests::run_tests);
|
|
selftests::register_test ("array_view-copy",
|
|
selftests::array_view_tests::run_copy_tests);
|
|
}
|