Commit 84fcf8bb authored by Jan Wilken Dörrie's avatar Jan Wilken Dörrie Committed by Commit Bot

Reland "[base] Introduce base::CheckedContiguousRange"

This is a reland of 83ced150

Original change's description:
> [base] Introduce base::CheckedContiguousRange
> 
> This change introduces base::CheckedContiguousRange, which similarly to
> base::span is a light-weight wrapper around a contiguous container
> performing bound CHECKs.
> 
> However, in contrast to base::span this class keeps a pointer to the
> underlying container, and thus is able to respond to changes to data()
> and size(), which base::span can't do.
> 
> Furthermore, this change provides a constexpr overload of base::data()
> for std::array and fixes a bug in CheckedContiguousIterator::operator-=.
> 
> Bug: 990059
> 
> Change-Id: I3fef91c7ef7874bf495ac2ab6dbaf3a8b02dab35
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1893858
> Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
> Reviewed-by: Chris Palmer <palmer@chromium.org>
> Reviewed-by: Daniel Cheng <dcheng@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#712120}

Bug: 990059
Change-Id: If7cfd16ca526930643e9fac7567f58ba9ef60b1f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1893069Reviewed-by: default avatarChris Palmer <palmer@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#712493}
parent 8312944a
...@@ -194,6 +194,7 @@ jumbo_component("base") { ...@@ -194,6 +194,7 @@ jumbo_component("base") {
"containers/adapters.h", "containers/adapters.h",
"containers/buffer_iterator.h", "containers/buffer_iterator.h",
"containers/checked_iterators.h", "containers/checked_iterators.h",
"containers/checked_range.h",
"containers/circular_deque.h", "containers/circular_deque.h",
"containers/flat_map.h", "containers/flat_map.h",
"containers/flat_set.h", "containers/flat_set.h",
...@@ -2529,6 +2530,7 @@ test("base_unittests") { ...@@ -2529,6 +2530,7 @@ test("base_unittests") {
"component_export_unittest.cc", "component_export_unittest.cc",
"containers/adapters_unittest.cc", "containers/adapters_unittest.cc",
"containers/buffer_iterator_unittest.cc", "containers/buffer_iterator_unittest.cc",
"containers/checked_range_unittest.cc",
"containers/circular_deque_unittest.cc", "containers/circular_deque_unittest.cc",
"containers/flat_map_unittest.cc", "containers/flat_map_unittest.cc",
"containers/flat_set_unittest.cc", "containers/flat_set_unittest.cc",
......
...@@ -38,9 +38,9 @@ class CheckedContiguousIterator { ...@@ -38,9 +38,9 @@ class CheckedContiguousIterator {
constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) = constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) =
default; default;
// Converting constructor allowing conversions like CRAI<T> to CRAI<const T>, // Converting constructor allowing conversions like CCI<T> to CCI<const T>,
// but disallowing CRAI<const T> to CRAI<T> or CRAI<Derived> to CRAI<Base>, // but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which
// which are unsafe. Furthermore, this is the same condition as used by the // are unsafe. Furthermore, this is the same condition as used by the
// converting constructors of std::span<T> and std::unique_ptr<T[]>. // converting constructors of std::span<T> and std::unique_ptr<T[]>.
// See https://wg21.link/n4042 for details. // See https://wg21.link/n4042 for details.
template < template <
...@@ -108,7 +108,7 @@ class CheckedContiguousIterator { ...@@ -108,7 +108,7 @@ class CheckedContiguousIterator {
return *this; return *this;
} }
constexpr CheckedContiguousIterator& operator--(int) { constexpr CheckedContiguousIterator operator--(int) {
CheckedContiguousIterator old = *this; CheckedContiguousIterator old = *this;
--*this; --*this;
return old; return old;
...@@ -132,9 +132,9 @@ class CheckedContiguousIterator { ...@@ -132,9 +132,9 @@ class CheckedContiguousIterator {
constexpr CheckedContiguousIterator& operator-=(difference_type rhs) { constexpr CheckedContiguousIterator& operator-=(difference_type rhs) {
if (rhs < 0) { if (rhs < 0) {
CHECK_LE(rhs, end_ - current_); CHECK_LE(-rhs, end_ - current_);
} else { } else {
CHECK_LE(-rhs, current_ - start_); CHECK_LE(rhs, current_ - start_);
} }
current_ -= rhs; current_ -= rhs;
return *this; return *this;
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CONTAINERS_CHECKED_RANGE_H_
#define BASE_CONTAINERS_CHECKED_RANGE_H_
#include <stddef.h>
#include <iterator>
#include <type_traits>
#include "base/containers/checked_iterators.h"
#include "base/stl_util.h"
namespace base {
// CheckedContiguousRange is a light-weight wrapper around a container modeling
// the ContiguousContainer requirement [1, 2]. Effectively this means that the
// container stores its elements contiguous in memory. Furthermore, it is
// expected that base::data(container) and base::size(container) are valid
// expressions, and that data() + idx is dereferencable for all idx in the
// range[0, size()). In the standard library this includes the containers
// std::string, std::vector and std::array, but other containers like
// std::initializer_list and C arrays are supported as well.
//
// In general this class is in nature quite similar to base::span, and its API
// is inspired by it. However, one important difference is that this class
// stores a pointer to the underlying container (as opposed to just storing its
// data() and size()), and thus is able to deal with changes to the container,
// such as removing or adding elements.
//
// Note however that this class still does not extend the life time of the
// underlying container, and thus callers need to make sure that the container
// outlives the view to avoid dangling pointers and references.
//
// Lastly, this class leverages base::CheckedContiguousIterator to perform
// bounds CHECKs, causing program termination when e.g. dereferencing the end
// iterator.
//
// [1] https://en.cppreference.com/w/cpp/named_req/ContiguousContainer
// [2]
// https://eel.is/c++draft/container.requirements.general#def:contiguous_container
template <typename ContiguousContainer>
class CheckedContiguousRange {
public:
using element_type = std::remove_pointer_t<decltype(
base::data(std::declval<ContiguousContainer&>()))>;
using value_type = std::remove_cv_t<element_type>;
using reference = element_type&;
using const_reference = const element_type&;
using pointer = element_type*;
using const_pointer = const element_type*;
using iterator = CheckedContiguousIterator<element_type>;
using const_iterator = CheckedContiguousConstIterator<element_type>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using difference_type = typename iterator::difference_type;
using size_type = size_t;
static_assert(!std::is_reference<ContiguousContainer>::value,
"Error: ContiguousContainer can not be a reference.");
// Required for converting constructor below.
template <typename Container>
friend class CheckedContiguousRange;
constexpr CheckedContiguousRange() noexcept = default;
// Templated constructor restricted to possibly rvref qualified versions of
// ContiguousContainer. This makes sure it does not shadow the auto generated
// copy and move constructors.
template <int&... ExplicitArgumentBarrier,
typename Container,
typename = std::enable_if_t<std::is_same<
std::remove_cv_t<std::remove_reference_t<ContiguousContainer>>,
std::remove_cv_t<std::remove_reference_t<Container>>>::value>>
constexpr CheckedContiguousRange(Container&& container) noexcept
: container_(&container) {}
// Converting constructor allowing conversions like CCR<C> to CCR<const C>,
// but disallowing CCR<const C> to CCR<C> or CCR<Derived[]> to CCR<Base[]>,
// which are unsafe. Furthermore, this is the same condition as used by the
// converting constructors of std::span<T> and std::unique_ptr<T[]>.
// See https://wg21.link/n4042 for details.
template <int&... ExplicitArgumentBarrier,
typename Container,
typename = std::enable_if_t<std::is_convertible<
typename CheckedContiguousRange<Container>::element_type (*)[],
element_type (*)[]>::value>>
constexpr CheckedContiguousRange(
CheckedContiguousRange<Container> range) noexcept
: container_(range.container_) {}
constexpr iterator begin() const noexcept {
return iterator(data(), data(), data() + size());
}
constexpr iterator end() const noexcept {
return iterator(data(), data() + size(), data() + size());
}
constexpr const_iterator cbegin() const noexcept { return begin(); }
constexpr const_iterator cend() const noexcept { return end(); }
constexpr reverse_iterator rbegin() const noexcept {
return reverse_iterator(end());
}
constexpr reverse_iterator rend() const noexcept {
return reverse_iterator(begin());
}
constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
constexpr const_reverse_iterator crend() const noexcept { return rend(); }
constexpr reference front() const noexcept { return *begin(); }
constexpr reference back() const noexcept { return *(end() - 1); }
constexpr reference operator[](size_type idx) const noexcept {
return *(begin() + idx);
}
constexpr pointer data() const noexcept { return base::data(*container_); }
constexpr const_pointer cdata() const noexcept { return data(); }
constexpr size_type size() const noexcept { return base::size(*container_); }
constexpr bool empty() const noexcept { return base::empty(*container_); }
private:
ContiguousContainer* container_ = nullptr;
};
// Utility functions helping to create const ranges and performing automatic
// type deduction.
template <typename ContiguousContainer>
using CheckedContiguousConstRange =
CheckedContiguousRange<const ContiguousContainer>;
template <int&... ExplicitArgumentBarrier, typename ContiguousContainer>
constexpr auto MakeCheckedContiguousRange(
ContiguousContainer&& container) noexcept {
return CheckedContiguousRange<std::remove_reference_t<ContiguousContainer>>(
std::forward<ContiguousContainer>(container));
}
template <int&... ExplicitArgumentBarrier, typename ContiguousContainer>
constexpr auto MakeCheckedContiguousConstRange(
ContiguousContainer&& container) noexcept {
return CheckedContiguousConstRange<
std::remove_reference_t<ContiguousContainer>>(
std::forward<ContiguousContainer>(container));
}
} // namespace base
#endif // BASE_CONTAINERS_CHECKED_RANGE_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/containers/checked_range.h"
#include <algorithm>
#include <array>
#include <initializer_list>
#include <type_traits>
#include "base/strings/string_piece.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
TEST(CheckedContiguousRange, Constructor_Vector) {
std::vector<int> vector = {1, 2, 3, 4, 5};
CheckedContiguousRange<std::vector<int>> range(vector);
EXPECT_EQ(vector.data(), range.data());
EXPECT_EQ(vector.size(), range.size());
}
TEST(CheckedContiguousRange, Constructor_String) {
std::string str = "Hello World";
CheckedContiguousRange<std::string> range(str);
EXPECT_EQ(str.data(), range.data());
EXPECT_EQ(str.size(), range.size());
}
TEST(CheckedContiguousRange, Constructor_Array) {
static constexpr int array[] = {1, 2, 3, 4, 5};
constexpr CheckedContiguousRange<const int[5]> range(array);
static_assert(data(array) == range.data(), "");
static_assert(size(array) == range.size(), "");
}
TEST(CheckedContiguousRange, Constructor_StdArray) {
static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
static_assert(data(array) == range.data(), "");
static_assert(size(array) == range.size(), "");
}
TEST(CheckedContiguousRange, Constructor_StringPiece) {
static constexpr base::StringPiece str = "Hello World";
constexpr CheckedContiguousRange<const base::StringPiece> range(str);
static_assert(str.data() == range.data(), "");
static_assert(str.size() == range.size(), "");
}
TEST(CheckedContiguousRange, Constructor_InitializerList) {
static constexpr std::initializer_list<int> il = {1, 2, 3, 4, 5};
constexpr CheckedContiguousRange<const std::initializer_list<int>> range(il);
static_assert(data(il) == range.data(), "");
static_assert(size(il) == range.size(), "");
}
TEST(CheckedContiguousRange, Constructor_Copy) {
std::vector<int> vector = {1, 2, 3, 4, 5};
CheckedContiguousRange<std::vector<int>> range(vector);
CheckedContiguousRange<std::vector<int>> copy(range);
EXPECT_EQ(vector.data(), copy.data());
EXPECT_EQ(vector.size(), copy.size());
}
TEST(CheckedContiguousRange, Constructor_Move) {
std::vector<int> vector = {1, 2, 3, 4, 5};
CheckedContiguousRange<std::vector<int>> range(vector);
CheckedContiguousRange<std::vector<int>> move(std::move(range));
EXPECT_EQ(vector.data(), move.data());
EXPECT_EQ(vector.size(), move.size());
}
TEST(CheckedContiguousRange, Copy_Assign) {
std::vector<int> vector = {1, 2, 3, 4, 5};
CheckedContiguousRange<std::vector<int>> range(vector);
CheckedContiguousRange<std::vector<int>> copy;
copy = range;
EXPECT_EQ(vector.data(), copy.data());
EXPECT_EQ(vector.size(), copy.size());
}
TEST(CheckedContiguousRange, Move_Assign) {
std::vector<int> vector = {1, 2, 3, 4, 5};
CheckedContiguousRange<std::vector<int>> range(vector);
CheckedContiguousRange<std::vector<int>> move;
move = std::move(range);
EXPECT_EQ(vector.data(), move.data());
EXPECT_EQ(vector.size(), move.size());
}
TEST(CheckedContiguousRange, Iterators) {
std::vector<int> vector;
CheckedContiguousRange<const std::vector<int>> range(vector);
// Check that all ranges by the iterators compare equal, even when elements
// are added.
for (size_t i = 0; i < 5; ++i) {
vector.push_back(i);
EXPECT_TRUE(
std::equal(vector.begin(), vector.end(), range.begin(), range.end()));
EXPECT_TRUE(std::equal(vector.cbegin(), vector.cend(), range.cbegin(),
range.cend()));
EXPECT_TRUE(std::equal(vector.rbegin(), vector.rend(), range.rbegin(),
range.rend()));
EXPECT_TRUE(std::equal(vector.crbegin(), vector.crend(), range.crbegin(),
range.crend()));
}
}
TEST(CheckedContiguousRange, Front) {
static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
static_assert(array.front() == range.front(), "");
}
TEST(CheckedContiguousRange, Back) {
static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
static_assert(array.back() == range.back(), "");
}
TEST(CheckedContiguousRange, OperatorAt_Constexpr) {
static constexpr std::array<int, 5> array = {1, 2, 3, 4, 5};
constexpr CheckedContiguousRange<const std::array<int, 5>> range(array);
static_assert(array[0] == range[0], "");
static_assert(array[1] == range[1], "");
static_assert(array[2] == range[2], "");
static_assert(array[3] == range[3], "");
static_assert(array[4] == range[4], "");
}
TEST(CheckedContiguousRange, Mutable_OperatorAt) {
std::vector<int> vector = {1, 2, 3, 4, 5};
CheckedContiguousRange<std::vector<int>> range(vector);
EXPECT_EQ(vector[0], range[0]);
range[0] = 2;
EXPECT_EQ(vector[0], 2);
}
TEST(CheckedContiguousRange, Mutable_Data) {
std::vector<int> vector = {3, 1, 4, 2, 5};
CheckedContiguousRange<std::vector<int>> range(vector);
EXPECT_FALSE(std::is_sorted(vector.begin(), vector.end()));
std::sort(range.data(), range.data() + range.size());
EXPECT_TRUE(std::is_sorted(vector.begin(), vector.end()));
}
TEST(CheckedContiguousRange, DataSizeEmpty_Constexpr) {
static constexpr std::array<int, 0> array = {};
constexpr CheckedContiguousRange<const std::array<int, 0>> range(array);
static_assert(data(array) == range.data(), "");
static_assert(data(array) == range.cdata(), "");
static_assert(size(array) == range.size(), "");
static_assert(range.empty(), "");
}
TEST(CheckedContiguousRange, MakeCheckedContiguousRange) {
using Vec = std::vector<int>;
static_assert(std::is_same<CheckedContiguousRange<Vec>,
decltype(MakeCheckedContiguousRange(
std::declval<Vec&>()))>::value,
"");
static_assert(std::is_same<CheckedContiguousRange<const Vec>,
decltype(MakeCheckedContiguousRange(
std::declval<const Vec&>()))>::value,
"");
static_assert(std::is_same<CheckedContiguousRange<Vec>,
decltype(MakeCheckedContiguousRange(
std::declval<Vec&&>()))>::value,
"");
static_assert(std::is_same<CheckedContiguousRange<const Vec>,
decltype(MakeCheckedContiguousRange(
std::declval<const Vec&&>()))>::value,
"");
}
TEST(CheckedContiguousRange, MakeCheckedContiguousConstRange) {
using Vec = std::vector<int>;
static_assert(std::is_same<CheckedContiguousRange<const Vec>,
decltype(MakeCheckedContiguousConstRange(
std::declval<Vec&>()))>::value,
"");
static_assert(std::is_same<CheckedContiguousRange<const Vec>,
decltype(MakeCheckedContiguousConstRange(
std::declval<const Vec&>()))>::value,
"");
static_assert(std::is_same<CheckedContiguousRange<const Vec>,
decltype(MakeCheckedContiguousConstRange(
std::declval<Vec&&>()))>::value,
"");
static_assert(std::is_same<CheckedContiguousRange<const Vec>,
decltype(MakeCheckedContiguousConstRange(
std::declval<const Vec&&>()))>::value,
"");
}
TEST(CheckedContiguousRange, Conversions) {
using T = std::vector<int>;
// Check that adding const is allowed, but removing const is not.
static_assert(std::is_convertible<CheckedContiguousRange<T>,
CheckedContiguousRange<const T>>::value,
"");
static_assert(!std::is_constructible<CheckedContiguousRange<T>,
CheckedContiguousRange<const T>>::value,
"");
struct Base {};
struct Derived : Base {};
// Check that trying to assign arrays from a derived class fails.
static_assert(
!std::is_constructible<CheckedContiguousRange<Base[5]>,
CheckedContiguousRange<Derived[5]>>::value,
"");
}
// Death checks can be very slow. Thus only enable them in release builds.
#ifdef NDEBUG
TEST(CheckedContiguousRange, OutOfBoundsDeath) {
std::vector<int> empty_vector;
CheckedContiguousRange<std::vector<int>> empty_range(empty_vector);
ASSERT_DEATH_IF_SUPPORTED(empty_range[0], "");
ASSERT_DEATH_IF_SUPPORTED(empty_range.begin()[0], "");
ASSERT_DEATH_IF_SUPPORTED(empty_range.end()[0], "");
ASSERT_DEATH_IF_SUPPORTED(*empty_range.begin(), "");
ASSERT_DEATH_IF_SUPPORTED(*empty_range.end(), "");
ASSERT_DEATH_IF_SUPPORTED(empty_range.front(), "");
ASSERT_DEATH_IF_SUPPORTED(empty_range.back(), "");
static constexpr int array[] = {0, 1, 2};
constexpr CheckedContiguousRange<const int[3]> range(array);
ASSERT_DEATH_IF_SUPPORTED(range[3], "");
ASSERT_DEATH_IF_SUPPORTED(range.begin()[-1], "");
ASSERT_DEATH_IF_SUPPORTED(range.begin()[3], "");
}
#endif // NDEBUG
} // namespace base
...@@ -1299,11 +1299,26 @@ TEST(SpanTest, EnsureConstexprGoodness) { ...@@ -1299,11 +1299,26 @@ TEST(SpanTest, EnsureConstexprGoodness) {
EXPECT_EQ(kArray[size], item); EXPECT_EQ(kArray[size], item);
} }
// Death checks can be very slow. Thus only enable them in release builds.
#ifdef NDEBUG
TEST(SpanTest, OutOfBoundsDeath) { TEST(SpanTest, OutOfBoundsDeath) {
constexpr span<int, 0> kEmptySpan; constexpr span<int, 0> kEmptySpan;
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin()[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin()[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end()[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end()[0], "");
ASSERT_DEATH_IF_SUPPORTED(--kEmptySpan.begin(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin()--, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin() -= 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin() - 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin() += (-1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin() + (-1), "");
ASSERT_DEATH_IF_SUPPORTED(++kEmptySpan.end(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end()++, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end() += 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end() + 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end() -= (-1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end() - (-1), "");
ASSERT_DEATH_IF_SUPPORTED(*kEmptySpan.end(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.first(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.first(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.last(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.last(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.subspan(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.subspan(1), "");
...@@ -1312,6 +1327,19 @@ TEST(SpanTest, OutOfBoundsDeath) { ...@@ -1312,6 +1327,19 @@ TEST(SpanTest, OutOfBoundsDeath) {
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin()[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin()[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end()[0], ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end()[0], "");
ASSERT_DEATH_IF_SUPPORTED(--kEmptyDynamicSpan.begin(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin()--, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin() -= 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin() - 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin() += (-1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin() + (-1), "");
ASSERT_DEATH_IF_SUPPORTED(++kEmptyDynamicSpan.end(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end()++, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end() += 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end() + 1, "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end() -= (-1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end() - (-1), "");
ASSERT_DEATH_IF_SUPPORTED(*kEmptyDynamicSpan.end(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.front(), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.front(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.first(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.first(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.last(1), ""); ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.last(1), "");
...@@ -1324,9 +1352,23 @@ TEST(SpanTest, OutOfBoundsDeath) { ...@@ -1324,9 +1352,23 @@ TEST(SpanTest, OutOfBoundsDeath) {
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan[4], ""); ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan[4], "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[-1], ""); ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[-1], "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[3], ""); ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[3], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(10), ""); ASSERT_DEATH_IF_SUPPORTED(--kNonEmptyDynamicSpan.begin(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(1, 7), ""); ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()--, "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin() -= 1, "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin() - 1, "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin() += (-1), "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin() + (-1), "");
ASSERT_DEATH_IF_SUPPORTED(++kNonEmptyDynamicSpan.end(), "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.end()++, "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.end() += 1, "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.end() + 1, "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.end() -= (-1), "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.end() - (-1), "");
ASSERT_DEATH_IF_SUPPORTED(*kNonEmptyDynamicSpan.end(), "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(10), "");
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(1, 7), "");
} }
#endif // NDEBUG
TEST(SpanTest, IteratorIsRangeMoveSafe) { TEST(SpanTest, IteratorIsRangeMoveSafe) {
static constexpr int kArray[] = {1, 6, 1, 8, 0}; static constexpr int kArray[] = {1, 6, 1, 8, 0};
......
...@@ -144,6 +144,20 @@ constexpr const T* data(std::initializer_list<T> il) noexcept { ...@@ -144,6 +144,20 @@ constexpr const T* data(std::initializer_list<T> il) noexcept {
return il.begin(); return il.begin();
} }
// std::array::data() was not constexpr prior to C++17 [1].
// Hence these overloads are provided.
//
// [1] https://en.cppreference.com/w/cpp/container/array/data
template <typename T, size_t N>
constexpr T* data(std::array<T, N>& array) noexcept {
return !array.empty() ? &array[0] : nullptr;
}
template <typename T, size_t N>
constexpr const T* data(const std::array<T, N>& array) noexcept {
return !array.empty() ? &array[0] : nullptr;
}
// Returns a const reference to the underlying container of a container adapter. // Returns a const reference to the underlying container of a container adapter.
// Works for std::priority_queue, std::queue, and std::stack. // Works for std::priority_queue, std::queue, and std::stack.
template <class A> template <class A>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment