tint: Replace VectorRef with ConstVectorRef.

The elements of the VectorRef is now immutable, but can be
moved, if the caller relinquishes ownership by explicitly using
std::move() at the callsite.

Also add utils::Empty as a way of signalling that a vector should be
constructed with no elements. This is helpful in templated code where
{} cannot be used due to overload ambiguity.

Change-Id: I24a50a13956b0692771a8bc9046336ad46261562
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/97842
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton
2022-08-02 16:52:25 +00:00
committed by Dawn LUCI CQ
parent bdc619f5ef
commit 34d46731bb
12 changed files with 423 additions and 483 deletions

View File

@@ -119,40 +119,6 @@ auto Transform(const VectorRef<IN>& in, TRANSFORMER&& transform)
return result;
}
/// Transform performs an element-wise transformation of a vector reference.
/// @param in the input vector.
/// @param transform the transformation function with signature: `OUT(IN)`
/// @tparam N the small-array size of the returned Vector
/// @returns a new vector with each element of the source vector transformed by `transform`.
template <size_t N, typename IN, typename TRANSFORMER>
auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
-> Vector<decltype(transform(in[0])), N> {
const auto count = in.Length();
Vector<decltype(transform(in[0])), N> result;
result.Reserve(count);
for (size_t i = 0; i < count; ++i) {
result.Push(transform(in[i]));
}
return result;
}
/// Transform performs an element-wise transformation of a vector reference.
/// @param in the input vector.
/// @param transform the transformation function with signature: `OUT(IN, size_t)`
/// @tparam N the small-array size of the returned Vector
/// @returns a new vector with each element of the source vector transformed by `transform`.
template <size_t N, typename IN, typename TRANSFORMER>
auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
-> Vector<decltype(transform(in[0], 1u)), N> {
const auto count = in.Length();
Vector<decltype(transform(in[0], 1u)), N> result;
result.Reserve(count);
for (size_t i = 0; i < count; ++i) {
result.Push(transform(in[i], i));
}
return result;
}
/// TransformN performs an element-wise transformation of a vector, transforming and returning at
/// most `n` elements.
/// @param in the input vector.

View File

@@ -345,72 +345,5 @@ TEST(TransformTest, TransformVectorRefDifferentType) {
}
}
TEST(TransformTest, ConstVectorRefEmpty) {
const Vector<int, 4> empty{};
ConstVectorRef<int> ref(empty);
{
auto transformed = Transform<4>(ref, [](int) -> int {
[] { FAIL() << "Callback should not be called for empty vector"; }();
return 0;
});
CHECK_ELEMENT_TYPE(transformed, int);
EXPECT_EQ(transformed.Length(), 0u);
}
{
auto transformed = Transform<4>(ref, [](int, size_t) -> int {
[] { FAIL() << "Callback should not be called for empty vector"; }();
return 0;
});
CHECK_ELEMENT_TYPE(transformed, int);
EXPECT_EQ(transformed.Length(), 0u);
}
}
TEST(TransformTest, ConstVectorRefIdentity) {
const Vector<int, 4> input{1, 2, 3, 4};
ConstVectorRef<int> ref(input);
auto transformed = Transform<8>(ref, [](int i) { return i; });
CHECK_ELEMENT_TYPE(transformed, int);
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
}
TEST(TransformTest, ConstVectorRefIdentityWithIndex) {
const Vector<int, 4> input{1, 2, 3, 4};
ConstVectorRef<int> ref(input);
auto transformed = Transform<2>(ref, [](int i, size_t) { return i; });
CHECK_ELEMENT_TYPE(transformed, int);
EXPECT_THAT(transformed, testing::ElementsAre(1, 2, 3, 4));
}
TEST(TransformTest, ConstVectorRefIndex) {
const Vector<int, 4> input{10, 20, 30, 40};
ConstVectorRef<int> ref(input);
{
auto transformed = Transform<4>(ref, [](int, size_t idx) { return idx; });
CHECK_ELEMENT_TYPE(transformed, size_t);
EXPECT_THAT(transformed, testing::ElementsAre(0u, 1u, 2u, 3u));
}
}
TEST(TransformTest, TransformConstVectorRefSameType) {
const Vector<int, 4> input{1, 2, 3, 4};
ConstVectorRef<int> ref(input);
{
auto transformed = Transform<4>(ref, [](int i) { return i * 10; });
CHECK_ELEMENT_TYPE(transformed, int);
EXPECT_THAT(transformed, testing::ElementsAre(10, 20, 30, 40));
}
}
TEST(TransformTest, TransformConstVectorRefDifferentType) {
const Vector<int, 4> input{1, 2, 3, 4};
ConstVectorRef<int> ref(input);
{
auto transformed = Transform<4>(ref, [](int i) { return std::to_string(i); });
CHECK_ELEMENT_TYPE(transformed, std::string);
EXPECT_THAT(transformed, testing::ElementsAre("1", "2", "3", "4"));
}
}
} // namespace
} // namespace tint::utils

View File

@@ -32,12 +32,18 @@ namespace tint::utils {
template <typename>
class VectorRef;
template <typename>
class ConstVectorRef;
class VectorRef;
} // namespace tint::utils
namespace tint::utils {
/// A type used to indicate an empty array.
struct EmptyType {};
/// An instance of the EmptyType.
static constexpr EmptyType Empty;
/// A slice represents a contigious array of elements of type T.
template <typename T>
struct Slice {
@@ -164,6 +170,9 @@ class Vector {
/// Constructor
Vector() = default;
/// Constructor
Vector(EmptyType) {} // NOLINT(runtime/explicit)
/// Constructor
/// @param elements the elements to place into the vector
Vector(std::initializer_list<T> elements) {
@@ -217,10 +226,7 @@ class Vector {
/// Copy constructor from an immutable vector reference
/// @param other the vector reference to copy
explicit Vector(const ConstVectorRef<T>& other) { Copy(other.slice_); }
/// Move constructor from an immutable vector reference (invalid)
Vector(ConstVectorRef<T>&&) = delete; // NOLINT(runtime/explicit)
explicit Vector(const VectorRef<T>& other) { Copy(other.slice_); }
/// Destructor
~Vector() { ClearAndFree(); }
@@ -263,6 +269,26 @@ class Vector {
return *this;
}
/// Assignment operator (differing N length)
/// @param other the vector reference to copy
/// @returns this vector so calls can be chained
Vector& operator=(const VectorRef<T>& other) {
if (&other.slice_ != &impl_.slice) {
Copy(other.slice_);
}
return *this;
}
/// Move operator (differing N length)
/// @param other the vector reference to copy
/// @returns this vector so calls can be chained
Vector& operator=(VectorRef<T>&& other) {
if (&other.slice_ != &impl_.slice) {
MoveOrCopy(std::move(other));
}
return *this;
}
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
@@ -367,7 +393,12 @@ class Vector {
/// Removes and returns the last element from the vector.
/// @returns the popped element
T Pop() { return std::move(impl_.slice.data[--impl_.slice.len]); }
T Pop() {
auto& el = impl_.slice.data[--impl_.slice.len];
auto val = std::move(el);
el.~T();
return val;
}
/// @returns true if the vector is empty.
bool IsEmpty() const { return impl_.slice.len == 0; }
@@ -419,7 +450,7 @@ class Vector {
/// Friend class
template <typename>
friend class ConstVectorRef;
friend class VectorRef;
/// The slice type used by this vector
using Slice = utils::Slice<T>;
@@ -573,11 +604,10 @@ Vector(Ts...) -> Vector<VectorCommonType<Ts...>, sizeof...(Ts)>;
/// VectorRef is a weak reference to a Vector, used to pass vectors as parameters, avoiding copies
/// between the caller and the callee. VectorRef can accept a Vector of any 'N' value, decoupling
/// the caller's vector internal size from the callee's vector size.
///
/// A VectorRef tracks the usage of moves either side of the call. If at the call site, a Vector
/// argument is moved to a VectorRef parameter, and within the callee, the VectorRef parameter is
/// moved to a Vector, then the Vector heap allocation will be moved. For example:
/// the caller's vector internal size from the callee's vector size. A VectorRef tracks the usage of
/// moves either side of the call. If at the call site, a Vector argument is moved to a VectorRef
/// parameter, and within the callee, the VectorRef parameter is moved to a Vector, then the Vector
/// heap allocation will be moved. For example:
///
/// ```
/// void func_a() {
@@ -596,12 +626,30 @@ class VectorRef {
/// The slice type used by this vector reference
using Slice = utils::Slice<T>;
/// @returns an empty slice.
static Slice& EmptySlice() {
static Slice empty;
return empty;
}
public:
/// Constructor - empty reference
VectorRef() : slice_(EmptySlice()) {}
/// Constructor
VectorRef(EmptyType) : slice_(EmptySlice()) {} // NOLINT(runtime/explicit)
/// Constructor from a Vector
/// @param vector the vector to create a reference of
template <size_t N>
VectorRef(Vector<T, N>& vector) // NOLINT(runtime/explicit)
: slice_(vector.impl_.slice), can_move_(false) {}
: slice_(vector.impl_.slice) {}
/// Constructor from a const Vector
/// @param vector the vector to create a reference of
template <size_t N>
VectorRef(const Vector<T, N>& vector) // NOLINT(runtime/explicit)
: slice_(const_cast<Slice&>(vector.impl_.slice)) {}
/// Constructor from a moved Vector
/// @param vector the vector being moved
@@ -611,7 +659,7 @@ class VectorRef {
/// Copy constructor
/// @param other the vector reference
VectorRef(const VectorRef& other) : slice_(other.slice_), can_move_(false) {}
VectorRef(const VectorRef& other) : slice_(other.slice_) {}
/// Move constructor
/// @param other the vector reference
@@ -621,7 +669,7 @@ class VectorRef {
/// @param other the other vector reference
template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
VectorRef(const VectorRef<U>& other) // NOLINT(runtime/explicit)
: slice_(*ReinterpretSlice<T>(&other.slice_)), can_move_(false) {}
: slice_(*ReinterpretSlice<T>(&other.slice_)) {}
/// Move constructor with covariance / const conversion
/// @param other the vector reference
@@ -634,7 +682,7 @@ class VectorRef {
/// @see CanReinterpretSlice for rules about conversion
template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
VectorRef(Vector<U, N>& vector) // NOLINT(runtime/explicit)
: slice_(*ReinterpretSlice<T>(&vector.impl_.slice)), can_move_(false) {}
: slice_(*ReinterpretSlice<T>(&vector.impl_.slice)) {}
/// Constructor from a moved Vector with covariance / const conversion
/// @param vector the vector to create a reference of
@@ -643,11 +691,6 @@ class VectorRef {
VectorRef(Vector<U, N>&& vector) // NOLINT(runtime/explicit)
: slice_(*ReinterpretSlice<T>(&vector.impl_.slice)), can_move_(vector.impl_.CanMove()) {}
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
T& operator[](size_t i) { return slice_[i]; }
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
@@ -710,7 +753,7 @@ class VectorRef {
/// Friend class
template <typename>
friend class ConstVectorRef;
friend class VectorRef;
/// The slice of the vector being referenced.
Slice& slice_;
@@ -718,99 +761,6 @@ class VectorRef {
bool can_move_ = false;
};
/// ConstVectorRef is a weak, immutable reference to a Vector, used to pass vectors as parameters,
/// avoiding copies between the caller and the callee. VectorRef can accept a Vector of any 'N'
/// value, decoupling the caller's vector internal size from the callee's vector size.
template <typename T>
class ConstVectorRef {
/// The slice type used by this vector reference
using Slice = utils::Slice<T>;
public:
/// Constructor from a Vector.
/// @param vector the vector reference
template <size_t N>
ConstVectorRef(const Vector<T, N>& vector) // NOLINT(runtime/explicit)
: slice_(vector.impl_.slice) {}
/// Copy constructor
/// @param other the vector reference
ConstVectorRef(const ConstVectorRef& other) = default;
/// Conversion constructor to convert from a non-const to const vector reference
/// @param other the vector reference
ConstVectorRef(const VectorRef<T>& other) : slice_(other.slice_) {} // NOLINT(runtime/explicit)
/// Move constructor. Deleted as this won't move anything.
ConstVectorRef(ConstVectorRef&&) = delete;
/// Constructor from a Vector with covariance / const conversion
/// @param vector the vector to create a reference of
/// @see CanReinterpretSlice for rules about conversion
template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
ConstVectorRef(const Vector<U, N>& vector) // NOLINT(runtime/explicit)
: slice_(*ReinterpretSlice<T>(&vector.impl_.slice)) {}
/// Constructor from a VectorRef with covariance / const conversion
/// @param other the vector reference
/// @see CanReinterpretSlice for rules about conversion
template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
ConstVectorRef(const VectorRef<U>& other) // NOLINT(runtime/explicit)
: slice_(*ReinterpretSlice<T>(&other.slice_)) {}
/// Constructor from a ConstVectorRef with covariance / const conversion
/// @param other the vector reference
/// @see CanReinterpretSlice for rules about conversion
template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
ConstVectorRef(const ConstVectorRef<U>& other) // NOLINT(runtime/explicit)
: slice_(*ReinterpretSlice<T>(&other.slice_)) {}
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
const T& operator[](size_t i) const { return slice_[i]; }
/// @return the number of elements in the vector
size_t Length() const { return slice_.len; }
/// @return the number of elements that the vector could hold before a heap allocation needs to
/// be made
size_t Capacity() const { return slice_.cap; }
/// @returns true if the vector is empty.
bool IsEmpty() const { return slice_.len == 0; }
/// @returns a reference to the first element in the vector
const T& Front() const { return slice_.Front(); }
/// @returns a reference to the last element in the vector
const T& Back() const { return slice_.Back(); }
/// @returns a pointer to the first element in the vector
const T* begin() const { return slice_.begin(); }
/// @returns a pointer to one past the last element in the vector
const T* end() const { return slice_.end(); }
/// @returns a reverse iterator starting with the last element in the vector
auto rbegin() const { return slice_.rbegin(); }
/// @returns the end for a reverse iterator
auto rend() const { return slice_.rend(); }
private:
/// Friend class
template <typename, size_t>
friend class Vector;
/// Friend class
template <typename>
friend class ConstVectorRef;
/// The slice of the vector being referenced.
const Slice& slice_;
};
/// Helper for converting a Vector to a std::vector.
/// @note This helper exists to help code migration. Avoid if possible.
template <typename T, size_t N>

View File

@@ -105,12 +105,24 @@ TEST(TintVectorTest, SmallArray_Empty) {
EXPECT_EQ(vec.Capacity(), 2u);
}
TEST(TintVectorTest, Empty_NoSmallArray) {
TEST(TintVectorTest, NoSmallArray) {
Vector<int, 0> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 0u);
}
TEST(TintVectorTest, Empty_SmallArray_Empty) {
Vector<int, 2> vec(Empty);
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 2u);
}
TEST(TintVectorTest, Empty_NoSmallArray) {
Vector<int, 0> vec(Empty);
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 0u);
}
TEST(TintVectorTest, InitializerList_NoSpill) {
Vector<std::string, 2> vec{"one", "two"};
EXPECT_EQ(vec.Length(), 2u);
@@ -800,7 +812,7 @@ TEST(TintVectorTest, RepeatMoveAssign_NoSpill) {
EXPECT_TRUE(AllInternallyHeld(vec));
}
TEST(TintVectorTest, DoubleMoveAssign_WithSpill) {
TEST(TintVectorTest, RepeatMoveAssign_WithSpill) {
Vector<std::string, 1> vec_a{"hello", "world"};
Vector<std::string, 1> vec_b{"Ciao", "mondo"};
Vector<std::string, 1> vec_c{"bonjour", "le", "monde"};
@@ -816,6 +828,288 @@ TEST(TintVectorTest, DoubleMoveAssign_WithSpill) {
EXPECT_TRUE(AllExternallyHeld(vec));
}
TEST(TintVectorTest, CopyAssignRef_NoSpill_N2_to_N2) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 2> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_WithSpill_N2_to_N2) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 2> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_NoSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 1> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_WithSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 1> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_NoSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 3> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_WithSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 3> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_NoSpill_N2_to_N0) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 0> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_WithSpill_N2_to_N0) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 0> vec_b;
vec_b = ref;
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, CopyAssignRef_Self_NoSpill) {
Vector<std::string, 2> vec{"hello", "world"};
VectorRef<std::string> ref{std::move(vec)};
vec = ref;
EXPECT_EQ(vec.Length(), 2u);
EXPECT_EQ(vec.Capacity(), 2u);
EXPECT_EQ(vec[0], "hello");
EXPECT_EQ(vec[1], "world");
EXPECT_TRUE(AllInternallyHeld(vec));
}
TEST(TintVectorTest, CopyAssignRef_Self_WithSpill) {
Vector<std::string, 1> vec{"hello", "world"};
VectorRef<std::string> ref{std::move(vec)};
vec = ref;
EXPECT_EQ(vec.Length(), 2u);
EXPECT_EQ(vec.Capacity(), 2u);
EXPECT_EQ(vec[0], "hello");
EXPECT_EQ(vec[1], "world");
EXPECT_TRUE(AllExternallyHeld(vec));
}
TEST(TintVectorTest, MoveAssignRef_NoSpill_N2_to_N2) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 2> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_WithSpill_N2_to_N2) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 2> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_NoSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 1> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_SpillSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 1> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_NoSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 3> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_WithSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 3> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_NoSpill_N2_to_N0) {
Vector<std::string, 2> vec_a{"hello", "world"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 0> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_WithSpill_N2_to_N0) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
VectorRef<std::string> ref{std::move(vec_a)};
Vector<std::string, 0> vec_b;
vec_b = std::move(ref);
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
EXPECT_EQ(vec_b[1], "world");
EXPECT_EQ(vec_b[2], "spill");
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
TEST(TintVectorTest, MoveAssignRef_Self_NoSpill) {
Vector<std::string, 2> vec{"hello", "world"};
VectorRef<std::string> ref{std::move(vec)};
vec = std::move(ref);
EXPECT_EQ(vec.Length(), 2u);
EXPECT_EQ(vec.Capacity(), 2u);
EXPECT_EQ(vec[0], "hello");
EXPECT_EQ(vec[1], "world");
EXPECT_TRUE(AllInternallyHeld(vec));
}
TEST(TintVectorTest, MoveAssignRef_Self_WithSpill) {
Vector<std::string, 1> vec{"hello", "world"};
VectorRef<std::string> ref{std::move(vec)};
vec = std::move(ref);
EXPECT_EQ(vec.Length(), 2u);
EXPECT_EQ(vec.Capacity(), 2u);
EXPECT_EQ(vec[0], "hello");
EXPECT_EQ(vec[1], "world");
EXPECT_TRUE(AllExternallyHeld(vec));
}
TEST(TintVectorTest, RepeatMoveAssignRef_NoSpill) {
Vector<std::string, 3> vec_a{"hello", "world"};
Vector<std::string, 3> vec_b{"Ciao", "mondo"};
Vector<std::string, 3> vec_c{"Bonjour", "le", "monde"};
VectorRef<std::string> ref_a{std::move(vec_a)};
VectorRef<std::string> ref_b{std::move(vec_b)};
VectorRef<std::string> ref_c{std::move(vec_c)};
Vector<std::string, 3> vec;
vec = std::move(ref_a);
vec = std::move(ref_b);
vec = std::move(ref_c);
EXPECT_EQ(vec.Length(), 3u);
EXPECT_EQ(vec.Capacity(), 3u);
EXPECT_EQ(vec[0], "Bonjour");
EXPECT_EQ(vec[1], "le");
EXPECT_EQ(vec[2], "monde");
EXPECT_TRUE(AllInternallyHeld(vec));
}
TEST(TintVectorTest, RepeatMoveAssignRef_WithSpill) {
Vector<std::string, 1> vec_a{"hello", "world"};
Vector<std::string, 1> vec_b{"Ciao", "mondo"};
Vector<std::string, 1> vec_c{"bonjour", "le", "monde"};
VectorRef<std::string> ref_a{std::move(vec_a)};
VectorRef<std::string> ref_b{std::move(vec_b)};
VectorRef<std::string> ref_c{std::move(vec_c)};
Vector<std::string, 1> vec;
vec = std::move(ref_a);
vec = std::move(ref_b);
vec = std::move(ref_c);
EXPECT_EQ(vec.Length(), 3u);
EXPECT_EQ(vec.Capacity(), 3u);
EXPECT_EQ(vec[0], "bonjour");
EXPECT_EQ(vec[1], "le");
EXPECT_EQ(vec[2], "monde");
EXPECT_TRUE(AllExternallyHeld(vec));
}
TEST(TintVectorTest, Index) {
Vector<std::string, 2> vec{"hello", "world"};
static_assert(!std::is_const_v<std::remove_reference_t<decltype(vec[0])>>);
@@ -1684,7 +1978,7 @@ TEST(TintVectorRefTest, MoveVector_UpcastAndAddConst) {
TEST(TintVectorRefTest, Index) {
Vector<std::string, 2> vec{"one", "two"};
VectorRef<std::string> vec_ref(vec);
static_assert(!std::is_const_v<std::remove_reference_t<decltype(vec_ref[0])>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(vec_ref[0])>>);
EXPECT_EQ(vec_ref[0], "one");
EXPECT_EQ(vec_ref[1], "two");
}
@@ -1755,206 +2049,6 @@ TEST(TintVectorRefTest, ConstBeginEnd) {
EXPECT_EQ(vec_ref.end(), &vec[0] + 3);
}
////////////////////////////////////////////////////////////////////////////////
// TintVectorConstRefTest
////////////////////////////////////////////////////////////////////////////////
TEST(TintVectorConstRefTest, CopyVectorConstRef) {
Vector<std::string, 1> vec_a{"one", "two"};
ConstVectorRef<std::string> vec_ref_a(vec_a);
ConstVectorRef<std::string> vec_ref_b(vec_ref_a);
Vector<std::string, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], "one");
EXPECT_EQ(vec_b[1], "two");
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVectorConstRef_Upcast) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
ConstVectorRef<C1*> vec_ref_a(vec_a);
ConstVectorRef<C0*> vec_ref_b(vec_ref_a); // Up-cast
Vector<C0*, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVectorConstRef_AddConst) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
ConstVectorRef<C1*> vec_ref_a(vec_a);
ConstVectorRef<const C1*> vec_ref_b(vec_ref_a); // Up-cast
Vector<const C1*, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVectorConstRef_UpcastAndAddConst) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
ConstVectorRef<C1*> vec_ref_a(vec_a);
ConstVectorRef<const C0*> vec_ref_b(vec_ref_a); // Up-cast
Vector<const C0*, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVector) {
Vector<std::string, 1> vec_a{"one", "two"};
ConstVectorRef<std::string> vec_ref(vec_a);
Vector<std::string, 2> vec_b(vec_ref);
EXPECT_EQ(vec_b[0], "one");
EXPECT_EQ(vec_b[1], "two");
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVector_Upcast) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
ConstVectorRef<C0*> vec_ref(vec_a);
EXPECT_EQ(vec_ref[0], &c2a);
EXPECT_EQ(vec_ref[1], &c2b);
Vector<C0*, 2> vec_b(vec_ref);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVector_AddConst) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
ConstVectorRef<const C1*> vec_ref(vec_a);
EXPECT_EQ(vec_ref[0], &c2a);
EXPECT_EQ(vec_ref[1], &c2b);
Vector<const C1*, 2> vec_b(vec_ref);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVectorRef_Upcast) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
VectorRef<C1*> vec_ref_a(vec_a);
ConstVectorRef<C0*> vec_ref_b(vec_ref_a);
EXPECT_EQ(vec_ref_b[0], &c2a);
EXPECT_EQ(vec_ref_b[1], &c2b);
Vector<C0*, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVectorRef_AddConst) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
VectorRef<C1*> vec_ref_a(vec_a);
ConstVectorRef<const C1*> vec_ref_b(vec_ref_a);
EXPECT_EQ(vec_ref_b[0], &c2a);
EXPECT_EQ(vec_ref_b[1], &c2b);
Vector<const C1*, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, CopyVectorRef_UpcastAndAddConst) {
C2a c2a;
C2b c2b;
Vector<C1*, 1> vec_a{&c2a, &c2b};
VectorRef<C1*> vec_ref_a(vec_a);
ConstVectorRef<const C0*> vec_ref_b(vec_ref_a);
EXPECT_EQ(vec_ref_b[0], &c2a);
EXPECT_EQ(vec_ref_b[1], &c2b);
Vector<const C0*, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], &c2a);
EXPECT_EQ(vec_b[1], &c2b);
EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, Index) {
Vector<std::string, 2> vec{"one", "two"};
ConstVectorRef<std::string> vec_ref(vec);
static_assert(std::is_const_v<std::remove_reference_t<decltype(vec_ref[0])>>);
EXPECT_EQ(vec_ref[0], "one");
EXPECT_EQ(vec_ref[1], "two");
}
TEST(TintVectorConstRefTest, ConstIndex) {
Vector<std::string, 2> vec{"one", "two"};
const ConstVectorRef<std::string> vec_ref(vec);
static_assert(std::is_const_v<std::remove_reference_t<decltype(vec_ref[0])>>);
EXPECT_EQ(vec_ref[0], "one");
EXPECT_EQ(vec_ref[1], "two");
}
TEST(TintVectorConstRefTest, Length) {
Vector<std::string, 2> vec{"one", "two", "three"};
ConstVectorRef<std::string> vec_ref(vec);
EXPECT_EQ(vec_ref.Length(), 3u);
}
TEST(TintVectorConstRefTest, Capacity) {
Vector<std::string, 5> vec{"one", "two", "three"};
ConstVectorRef<std::string> vec_ref(vec);
EXPECT_EQ(vec_ref.Capacity(), 5u);
}
TEST(TintVectorConstRefTest, IsEmpty) {
Vector<std::string, 1> vec;
ConstVectorRef<std::string> vec_ref(vec);
EXPECT_TRUE(vec_ref.IsEmpty());
vec.Push("one");
EXPECT_FALSE(vec_ref.IsEmpty());
vec.Pop();
EXPECT_TRUE(vec_ref.IsEmpty());
}
TEST(TintVectorConstRefTest, FrontBack) {
Vector<std::string, 3> vec{"front", "mid", "back"};
ConstVectorRef<std::string> vec_ref(vec);
static_assert(std::is_const_v<std::remove_reference_t<decltype(vec_ref.Front())>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(vec_ref.Back())>>);
EXPECT_EQ(vec_ref.Front(), "front");
EXPECT_EQ(vec_ref.Back(), "back");
}
TEST(TintVectorConstRefTest, ConstFrontBack) {
Vector<std::string, 3> vec{"front", "mid", "back"};
const ConstVectorRef<std::string> vec_ref(vec);
static_assert(std::is_const_v<std::remove_reference_t<decltype(vec_ref.Front())>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(vec_ref.Back())>>);
EXPECT_EQ(vec_ref.Front(), "front");
EXPECT_EQ(vec_ref.Back(), "back");
}
TEST(TintVectorConstRefTest, BeginEnd) {
Vector<std::string, 3> vec{"front", "mid", "back"};
ConstVectorRef<std::string> vec_ref(vec);
static_assert(std::is_const_v<std::remove_reference_t<decltype(*vec_ref.begin())>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(*vec_ref.end())>>);
EXPECT_EQ(vec_ref.begin(), &vec[0]);
EXPECT_EQ(vec_ref.end(), &vec[0] + 3);
}
TEST(TintVectorConstRefTest, ConstBeginEnd) {
Vector<std::string, 3> vec{"front", "mid", "back"};
const ConstVectorRef<std::string> vec_ref(vec);
static_assert(std::is_const_v<std::remove_reference_t<decltype(*vec_ref.begin())>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(*vec_ref.end())>>);
EXPECT_EQ(vec_ref.begin(), &vec[0]);
EXPECT_EQ(vec_ref.end(), &vec[0] + 3);
}
} // namespace
} // namespace tint::utils