Commit 9269f9eb authored by Jan Wilken Dörrie's avatar Jan Wilken Dörrie Committed by Commit Bot

[base] Add IsContiguousIterator type trait

This change implements the base::IsContiguousIterator type trait and
makes use of it in base::span's constructor to allow construction from
arbitrary contiguous iterators, and not just pointers.

Given the lack of std::contiguous_iterator_tag in C++14, the type trait
is especially instantiated for iterators that are known to be
contiguous. These include pointers, and the iterators for std::array,
std::string and std::vector<T> (for T != bool).

Lastly, this change adds an implementation of std::to_address, which
allows obtaining the underlying address from a pointer like type.

TBR=dcheng

Bug: 828324
Change-Id: I00d44c5edf8d86f811b7b2d32f07fa15e084d987
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2093215
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816543}
parent 7647648f
...@@ -183,6 +183,7 @@ component("base") { ...@@ -183,6 +183,7 @@ component("base") {
"containers/checked_iterators.h", "containers/checked_iterators.h",
"containers/checked_range.h", "containers/checked_range.h",
"containers/circular_deque.h", "containers/circular_deque.h",
"containers/contiguous_iterator.h",
"containers/flat_map.h", "containers/flat_map.h",
"containers/flat_set.h", "containers/flat_set.h",
"containers/flat_tree.h", "containers/flat_tree.h",
...@@ -2698,6 +2699,7 @@ test("base_unittests") { ...@@ -2698,6 +2699,7 @@ test("base_unittests") {
"containers/checked_iterators_unittest.cc", "containers/checked_iterators_unittest.cc",
"containers/checked_range_unittest.cc", "containers/checked_range_unittest.cc",
"containers/circular_deque_unittest.cc", "containers/circular_deque_unittest.cc",
"containers/contiguous_iterator_unittest.cc",
"containers/flat_map_unittest.cc", "containers/flat_map_unittest.cc",
"containers/flat_set_unittest.cc", "containers/flat_set_unittest.cc",
"containers/flat_tree_unittest.cc", "containers/flat_tree_unittest.cc",
......
// Copyright 2020 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_CONTIGUOUS_ITERATOR_H_
#define BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_
#include <iterator>
#include <type_traits>
#include "base/containers/checked_iterators.h"
namespace base {
namespace internal {
// By default a class is not a contiguous iterator.
template <typename T>
struct IsContiguousIteratorImpl : std::false_type {};
// A pointer to an object is a contiguous iterator.
//
// Reference: https://wg21.link/iterator.traits#5
template <typename T>
struct IsContiguousIteratorImpl<T*> : std::is_object<T> {};
#if defined(_LIBCPP_VERSION)
// libc++ uses std::__wrap_iter for STL iterators. For contiguous iterators
// these wrap raw pointers.
template <typename Iter>
struct IsContiguousIteratorImpl<std::__wrap_iter<Iter>>
: IsContiguousIteratorImpl<Iter> {};
#elif defined(__GLIBCXX__)
// libstdc++ uses __gnu_cxx::__normal_iterator for STL iterators. For contiguous
// iterators these wrap raw pointers.
template <typename Iter, typename Cont>
struct IsContiguousIteratorImpl<__gnu_cxx::__normal_iterator<Iter, Cont>>
: IsContiguousIteratorImpl<Iter> {};
#elif defined(_MSC_VER)
// Microsoft's STL does not have a single iterator wrapper class. Explicitly
// instantiate the template for all STL containers that are contiguous.
// All std::vector<T> have contiguous iterators, except for std::vector<bool>.
// Note: MSVC's std::vector<bool> uses `std::_Vb_iterator` as its iterator type,
// thus this won't treat these iterators as contiguous.
template <typename Vec>
struct IsContiguousIteratorImpl<std::_Vector_iterator<Vec>> : std::true_type {};
template <typename Vec>
struct IsContiguousIteratorImpl<std::_Vector_const_iterator<Vec>>
: std::true_type {};
// All std::array<T, N> have contiguous iterators.
template <typename T, size_t N>
struct IsContiguousIteratorImpl<std::_Array_iterator<T, N>> : std::true_type {};
template <typename T, size_t N>
struct IsContiguousIteratorImpl<std::_Array_const_iterator<T, N>>
: std::true_type {};
// All std::basic_string<CharT> have contiguous iterators.
template <typename Str>
struct IsContiguousIteratorImpl<std::_String_iterator<Str>> : std::true_type {};
template <typename Str>
struct IsContiguousIteratorImpl<std::_String_const_iterator<Str>>
: std::true_type {};
// Note: std::valarray<T> also has contiguous storage, but does not expose a
// nested iterator type. In MSVC's implementation `std::begin(valarray<T>)` is
// of type T*, thus it is already covered by the explicit instantiation for
// pointers above.
#endif
// base's CheckedContiguousIterator is a contiguous iterator as well.
template <typename T>
struct IsContiguousIteratorImpl<base::CheckedContiguousIterator<T>>
: std::true_type {};
} // namespace internal
// IsContiguousIterator is a type trait that determines whether a given type is
// a contiguous iterator. It is similar to C++20's contiguous_iterator concept,
// but due to a lack of the corresponding contiguous_iterator_tag relies on
// explicitly instantiating the type with iterators that are supposed to be
// contiguous iterators.
// References:
// - https://eel.is/c++draft/iterator.concept.contiguous
// - https://eel.is/c++draft/std.iterator.tags#lib:contiguous_iterator_tag
// - https://wg21.link/n4284
template <typename T>
struct IsContiguousIterator
: internal::IsContiguousIteratorImpl<
std::remove_cv_t<std::remove_reference_t<T>>> {};
} // namespace base
#endif // BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_
This diff is collapsed.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "base/check_op.h" #include "base/check_op.h"
#include "base/containers/checked_iterators.h" #include "base/containers/checked_iterators.h"
#include "base/containers/contiguous_iterator.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/template_util.h" #include "base/template_util.h"
...@@ -72,6 +73,15 @@ using IsNotCArray = negation<std::is_array<std::remove_reference_t<T>>>; ...@@ -72,6 +73,15 @@ using IsNotCArray = negation<std::is_array<std::remove_reference_t<T>>>;
template <typename From, typename To> template <typename From, typename To>
using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>; using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
template <typename Iter, typename T>
using IteratorHasConvertibleReferenceType =
IsLegalDataConversion<std::remove_reference_t<iter_reference_t<Iter>>, T>;
template <typename Iter, typename T>
using EnableIfCompatibleContiguousIterator = std::enable_if_t<
conjunction<IsContiguousIterator<Iter>,
IteratorHasConvertibleReferenceType<Iter, T>>::value>;
template <typename Container, typename T> template <typename Container, typename T>
using ContainerHasConvertibleData = IsLegalDataConversion< using ContainerHasConvertibleData = IsLegalDataConversion<
std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>, std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>,
...@@ -250,14 +260,19 @@ class span : public internal::ExtentStorage<Extent> { ...@@ -250,14 +260,19 @@ class span : public internal::ExtentStorage<Extent> {
static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent"); static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
} }
constexpr span(T* data, size_t size) noexcept template <typename It,
: ExtentStorage(size), data_(data) { typename = internal::EnableIfCompatibleContiguousIterator<It, T>>
CHECK(Extent == dynamic_extent || Extent == size); constexpr span(It first, size_t count) noexcept
: ExtentStorage(count), data_(base::to_address(first)) {
CHECK(Extent == dynamic_extent || Extent == count);
} }
// Artificially templatized to break ambiguity for span(ptr, 0). template <
template <typename = void> typename It,
constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) { typename End,
typename = internal::EnableIfCompatibleContiguousIterator<It, T>,
typename = std::enable_if_t<!std::is_convertible<End, size_t>::value>>
constexpr span(It begin, End end) noexcept : span(begin, end - begin) {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used. // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(begin <= end); CHECK(begin <= end);
} }
...@@ -442,14 +457,10 @@ as_writable_bytes(span<T, X> s) noexcept { ...@@ -442,14 +457,10 @@ as_writable_bytes(span<T, X> s) noexcept {
} }
// Type-deducing helpers for constructing a span. // Type-deducing helpers for constructing a span.
template <int&... ExplicitArgumentBarrier, typename T> template <int&... ExplicitArgumentBarrier, typename It, typename EndOrSize>
constexpr span<T> make_span(T* data, size_t size) noexcept { constexpr auto make_span(It it, EndOrSize end_or_size) noexcept {
return {data, size}; using T = std::remove_reference_t<iter_reference_t<It>>;
} return span<T>(it, end_or_size);
template <int&... ExplicitArgumentBarrier, typename T>
constexpr span<T> make_span(T* begin, T* end) noexcept {
return {begin, end};
} }
// make_span utility function that deduces both the span's value_type and extent // make_span utility function that deduces both the span's value_type and extent
...@@ -472,6 +483,15 @@ constexpr auto make_span(Container&& container) noexcept { ...@@ -472,6 +483,15 @@ constexpr auto make_span(Container&& container) noexcept {
// Note: This will CHECK that N indeed matches size(container). // Note: This will CHECK that N indeed matches size(container).
// //
// Usage: auto static_span = base::make_span<N>(...); // Usage: auto static_span = base::make_span<N>(...);
template <size_t N,
int&... ExplicitArgumentBarrier,
typename It,
typename EndOrSize>
constexpr auto make_span(It it, EndOrSize end_or_size) noexcept {
using T = std::remove_reference_t<iter_reference_t<It>>;
return span<T, N>(it, end_or_size);
}
template <size_t N, int&... ExplicitArgumentBarrier, typename Container> template <size_t N, int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept { constexpr auto make_span(Container&& container) noexcept {
using T = using T =
......
...@@ -54,7 +54,8 @@ TEST(SpanTest, DefaultConstructor) { ...@@ -54,7 +54,8 @@ TEST(SpanTest, DefaultConstructor) {
} }
TEST(SpanTest, ConstructFromDataAndSize) { TEST(SpanTest, ConstructFromDataAndSize) {
constexpr span<int> empty_span(nullptr, 0); constexpr int* kNull = nullptr;
constexpr span<int> empty_span(kNull, 0);
EXPECT_TRUE(empty_span.empty()); EXPECT_TRUE(empty_span.empty());
EXPECT_EQ(nullptr, empty_span.data()); EXPECT_EQ(nullptr, empty_span.data());
...@@ -75,21 +76,45 @@ TEST(SpanTest, ConstructFromDataAndSize) { ...@@ -75,21 +76,45 @@ TEST(SpanTest, ConstructFromDataAndSize) {
EXPECT_EQ(vector[i], static_span[i]); EXPECT_EQ(vector[i], static_span[i]);
} }
TEST(SpanTest, ConstructFromPointerPair) { TEST(SpanTest, ConstructFromIterAndSize) {
constexpr span<int> empty_span(nullptr, nullptr); constexpr int* kNull = nullptr;
constexpr span<int> empty_span(kNull, 0);
EXPECT_TRUE(empty_span.empty()); EXPECT_TRUE(empty_span.empty());
EXPECT_EQ(nullptr, empty_span.data()); EXPECT_EQ(nullptr, empty_span.data());
std::vector<int> vector = {1, 1, 2, 3, 5, 8}; std::vector<int> vector = {1, 1, 2, 3, 5, 8};
span<int> dynamic_span(vector.data(), vector.data() + vector.size() / 2); span<int> dynamic_span(vector.begin(), vector.size());
EXPECT_EQ(vector.data(), dynamic_span.data());
EXPECT_EQ(vector.size(), dynamic_span.size());
for (size_t i = 0; i < dynamic_span.size(); ++i)
EXPECT_EQ(vector[i], dynamic_span[i]);
span<int, 6> static_span(vector.begin(), vector.size());
EXPECT_EQ(vector.data(), static_span.data());
EXPECT_EQ(vector.size(), static_span.size());
for (size_t i = 0; i < static_span.size(); ++i)
EXPECT_EQ(vector[i], static_span[i]);
}
TEST(SpanTest, ConstructFromIterPair) {
constexpr int* kNull = nullptr;
constexpr span<int> empty_span(kNull, kNull);
EXPECT_TRUE(empty_span.empty());
EXPECT_EQ(nullptr, empty_span.data());
std::vector<int> vector = {1, 1, 2, 3, 5, 8};
span<int> dynamic_span(vector.begin(), vector.begin() + vector.size() / 2);
EXPECT_EQ(vector.data(), dynamic_span.data()); EXPECT_EQ(vector.data(), dynamic_span.data());
EXPECT_EQ(vector.size() / 2, dynamic_span.size()); EXPECT_EQ(vector.size() / 2, dynamic_span.size());
for (size_t i = 0; i < dynamic_span.size(); ++i) for (size_t i = 0; i < dynamic_span.size(); ++i)
EXPECT_EQ(vector[i], dynamic_span[i]); EXPECT_EQ(vector[i], dynamic_span[i]);
span<int, 3> static_span(vector.data(), vector.data() + vector.size() / 2); span<int, 3> static_span(vector.begin(), vector.begin() + vector.size() / 2);
EXPECT_EQ(vector.data(), static_span.data()); EXPECT_EQ(vector.data(), static_span.data());
EXPECT_EQ(vector.size() / 2, static_span.size()); EXPECT_EQ(vector.size() / 2, static_span.size());
......
...@@ -102,6 +102,22 @@ void WontCompile() { ...@@ -102,6 +102,22 @@ void WontCompile() {
span<int, 3> span = v; span<int, 3> span = v;
} }
#elif defined(NCTEST_STD_SET_ITER_SIZE_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<int>'"]
// A std::set() should not satisfy the requirements for conversion to a span.
void WontCompile() {
std::set<int> set;
span<int> span(set.begin(), 0);
}
#elif defined(NCTEST_STD_SET_ITER_ITER_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<int>'"]
// A std::set() should not satisfy the requirements for conversion to a span.
void WontCompile() {
std::set<int> set;
span<int> span(set.begin(), set.end());
}
#elif defined(NCTEST_STD_SET_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<int>'"] #elif defined(NCTEST_STD_SET_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<int>'"]
// A std::set() should not satisfy the requirements for conversion to a span. // A std::set() should not satisfy the requirements for conversion to a span.
...@@ -188,6 +204,13 @@ void WontCompile() { ...@@ -188,6 +204,13 @@ void WontCompile() {
span<uint8_t> bytes = as_writable_bytes(make_span(v)); span<uint8_t> bytes = as_writable_bytes(make_span(v));
} }
#elif defined(NCTEST_MAKE_SPAN_FROM_SET_ITER_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<T>'"]
// A std::set() should not satisfy the requirements for conversion to a span.
void WontCompile() {
std::set<int> set;
auto span = make_span(set.begin(), set.end());
}
#elif defined(NCTEST_MAKE_SPAN_FROM_SET_CONVERSION_DISALLOWED) // [r"fatal error: no matching function for call to 'data'"] #elif defined(NCTEST_MAKE_SPAN_FROM_SET_CONVERSION_DISALLOWED) // [r"fatal error: no matching function for call to 'data'"]
// A std::set() should not satisfy the requirements for conversion to a span. // A std::set() should not satisfy the requirements for conversion to a span.
...@@ -203,6 +226,28 @@ int WontCompile() { ...@@ -203,6 +226,28 @@ int WontCompile() {
span<int> s = make_span(v); span<int> s = make_span(v);
} }
#elif defined(NCTEST_STATIC_MAKE_SPAN_FROM_ITER_AND_SIZE_CHECKS) // [r"constexpr variable 'made_span' must be initialized by a constant expression"]
// The static make_span<N>(begin, size) should CHECK whether N matches size.
// This should result in compilation failures when evaluated at compile
// time.
int WontCompile() {
constexpr StringPiece str = "Foo";
// Intentional extent mismatch causing CHECK failure.
constexpr auto made_span = make_span<2>(str.begin(), 3);
}
#elif defined(NCTEST_STATIC_MAKE_SPAN_FROM_ITERS_CHECKS_SIZE) // [r"constexpr variable 'made_span' must be initialized by a constant expression"]
// The static make_span<N>(begin, end) should CHECK whether N matches end -
// begin. This should result in compilation failures when evaluated at compile
// time.
int WontCompile() {
constexpr StringPiece str = "Foo";
// Intentional extent mismatch causing CHECK failure.
constexpr auto made_span = make_span<2>(str.begin(), str.end());
}
#elif defined(NCTEST_STATIC_MAKE_SPAN_CHECKS_SIZE) // [r"constexpr variable 'made_span' must be initialized by a constant expression"] #elif defined(NCTEST_STATIC_MAKE_SPAN_CHECKS_SIZE) // [r"constexpr variable 'made_span' must be initialized by a constant expression"]
// The static make_span<N>(cont) should CHECK whether N matches size(cont). This // The static make_span<N>(cont) should CHECK whether N matches size(cont). This
......
...@@ -322,6 +322,23 @@ struct identity { ...@@ -322,6 +322,23 @@ struct identity {
using is_transparent = void; using is_transparent = void;
}; };
// Simplified C++14 implementation of C++20's std::to_address.
// Note: This does not consider specializations of pointer_traits<>::to_address,
// since that member function may only be present in C++20 and later.
//
// Reference: https://wg21.link/pointer.conversion#lib:to_address
template <typename T>
constexpr T* to_address(T* p) noexcept {
static_assert(!std::is_function<T>::value,
"Error: T must not be a function type.");
return p;
}
template <typename Ptr>
constexpr auto to_address(const Ptr& p) noexcept {
return to_address(p.operator->());
}
// 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>
......
...@@ -672,7 +672,8 @@ void ResourceFetcher::DidLoadResourceFromMemoryCache( ...@@ -672,7 +672,8 @@ void ResourceFetcher::DidLoadResourceFromMemoryCache(
if (resource->EncodedSize() > 0) { if (resource->EncodedSize() > 0) {
resource_load_observer_->DidReceiveData( resource_load_observer_->DidReceiveData(
request.InspectorId(), request.InspectorId(),
base::span<const char>(nullptr, resource->EncodedSize())); base::make_span(static_cast<const char*>(nullptr),
resource->EncodedSize()));
} }
resource_load_observer_->DidFinishLoading( resource_load_observer_->DidFinishLoading(
......
...@@ -1378,9 +1378,9 @@ void ResourceLoader::OnProgress(uint64_t delta) { ...@@ -1378,9 +1378,9 @@ void ResourceLoader::OnProgress(uint64_t delta) {
return; return;
if (auto* observer = fetcher_->GetResourceLoadObserver()) { if (auto* observer = fetcher_->GetResourceLoadObserver()) {
observer->DidReceiveData( observer->DidReceiveData(resource_->InspectorId(),
resource_->InspectorId(), base::make_span(static_cast<const char*>(nullptr),
base::span<const char>(nullptr, static_cast<size_t>(delta))); static_cast<size_t>(delta)));
} }
resource_->DidDownloadData(delta); resource_->DidDownloadData(delta);
} }
......
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