Skip to content

Commit

Permalink
[base] Add IsContiguousIterator type trait
Browse files Browse the repository at this point in the history
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: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816543}
  • Loading branch information
jdoerrie authored and Commit Bot committed Oct 13, 2020
1 parent 7647648 commit 9269f9e
Show file tree
Hide file tree
Showing 9 changed files with 480 additions and 23 deletions.
2 changes: 2 additions & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ component("base") {
"containers/checked_iterators.h",
"containers/checked_range.h",
"containers/circular_deque.h",
"containers/contiguous_iterator.h",
"containers/flat_map.h",
"containers/flat_set.h",
"containers/flat_tree.h",
Expand Down Expand Up @@ -2698,6 +2699,7 @@ test("base_unittests") {
"containers/checked_iterators_unittest.cc",
"containers/checked_range_unittest.cc",
"containers/circular_deque_unittest.cc",
"containers/contiguous_iterator_unittest.cc",
"containers/flat_map_unittest.cc",
"containers/flat_set_unittest.cc",
"containers/flat_tree_unittest.cc",
Expand Down
100 changes: 100 additions & 0 deletions base/containers/contiguous_iterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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_
247 changes: 247 additions & 0 deletions base/containers/contiguous_iterator_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
// 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.

#include "base/containers/contiguous_iterator.h"

#include <array>
#include <deque>
#include <forward_list>
#include <iterator>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <valarray>
#include <vector>

#include "base/containers/span.h"
#include "base/strings/string_piece.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

TEST(ContiguousIteratorTest, ForwardIterator) {
using ForwardIterator = std::forward_list<int>::iterator;
static_assert(std::is_same<std::forward_iterator_tag,
typename std::iterator_traits<
ForwardIterator>::iterator_category>::value,
"Error: The iterator_category of ForwardIterator is not "
"std::forward_iterator_tag.");
static_assert(
!IsContiguousIterator<ForwardIterator>::value,
"Error: ForwardIterator should not be considered a contiguous iterator.");
static_assert(!IsContiguousIterator<const ForwardIterator>::value,
"Error: const ForwardIterator should not be considered a "
"contiguous iterator.");
static_assert(!IsContiguousIterator<ForwardIterator&>::value,
"Error: ForwardIterator& should not be considered a contiguous "
"iterator.");
static_assert(!IsContiguousIterator<const ForwardIterator&>::value,
"Error: const ForwardIterator& should not be considered a "
"contiguous iterator.");
static_assert(
!IsContiguousIterator<std::reverse_iterator<ForwardIterator>>::value,
"Error: A reverse ForwardIterator should not be considered a "
"contiguous iterator.");
}

TEST(ContiguousIteratorTest, BidirectionalIterator) {
using BidirectionalIterator = std::set<int>::iterator;
static_assert(
std::is_same<std::bidirectional_iterator_tag,
typename std::iterator_traits<
BidirectionalIterator>::iterator_category>::value,
"Error: The iterator_category of BidirectionalIterator is not "
"std::bidirectional_iterator_tag.");
static_assert(!IsContiguousIterator<BidirectionalIterator>::value,
"Error: BidirectionalIterator should not be considered a "
"contiguous iterator.");
static_assert(!IsContiguousIterator<const BidirectionalIterator>::value,
"Error: const BidirectionalIterator should not be considered a "
"contiguous iterator.");
static_assert(!IsContiguousIterator<BidirectionalIterator&>::value,
"Error: BidirectionalIterator& should not be considered a "
"contiguous iterator.");
static_assert(!IsContiguousIterator<const BidirectionalIterator&>::value,
"Error: const BidirectionalIterator& should not be considered "
"a contiguous iterator.");
static_assert(!IsContiguousIterator<
std::reverse_iterator<BidirectionalIterator>>::value,
"Error: A reverse BidirectionalIterator should not be "
"considered a contiguous iterator.");
}

TEST(ContiguousIteratorTest, RandomAccessIterator) {
using RandomAccessIterator = std::deque<int>::iterator;
static_assert(
std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<
RandomAccessIterator>::iterator_category>::value,
"Error: The iterator_category of RandomAccessIterator is not "
"std::random_access_iterator_tag.");
static_assert(!IsContiguousIterator<RandomAccessIterator>::value,
"Error: RandomAccessIterator should not be considered a "
"contiguous iterator.");
static_assert(!IsContiguousIterator<const RandomAccessIterator>::value,
"Error: const RandomAccessIterator should not be considered a "
"contiguous iterator.");
static_assert(!IsContiguousIterator<RandomAccessIterator&>::value,
"Error: RandomAccessIterator& should not be considered a "
"contiguous iterator.");
static_assert(!IsContiguousIterator<const RandomAccessIterator&>::value,
"Error: const RandomAccessIterator& should not be considered "
"a contiguous iterator.");
static_assert(
!IsContiguousIterator<std::reverse_iterator<RandomAccessIterator>>::value,
"Error: A reverse RandomAccessIterator should not be "
"considered a contiguous iterator.");
}

TEST(ContiguousIterator, Pointer) {
static_assert(IsContiguousIterator<int*>::value,
"Error: int* should be considered a contiguous iterator.");

static_assert(
IsContiguousIterator<int* const>::value,
"Error: int* const should be considered a contiguous iterator.");

static_assert(!IsContiguousIterator<void (*)()>::value,
"Error: A function pointer should not be considered a "
"contiguous iterator.");
}

TEST(ContiguousIterator, Vector) {
static_assert(IsContiguousIterator<std::vector<int>::iterator>::value,
"Error: std::vector<int>::iterator should be considered a "
"contiguous iterator.");

static_assert(IsContiguousIterator<std::vector<int>::const_iterator>::value,
"Error: std::vector<int>::const_iterator should be considered "
"a contiguous iterator.");

static_assert(
!IsContiguousIterator<std::vector<int>::reverse_iterator>::value,
"Error: std::vector<int>::reverse_iterator should not be considered a "
"contiguous iterator.");

static_assert(
!IsContiguousIterator<std::vector<int>::const_reverse_iterator>::value,
"Error: std::vector<int>::const_reverse_iterator should not be "
"considered a contiguous iterator.");
}

TEST(ContiguousIterator, VectorBool) {
static_assert(!IsContiguousIterator<std::vector<bool>::iterator>::value,
"Error: std::vector<bool>::iterator should not be considered "
"a contiguous iterator.");

static_assert(!IsContiguousIterator<std::vector<bool>::const_iterator>::value,
"Error: std::vector<bool>::const_iterator should not be "
"considered a contiguous iterator.");

static_assert(
!IsContiguousIterator<std::vector<bool>::reverse_iterator>::value,
"Error: std::vector<bool>::reverse_iterator should not be considered a "
"contiguous iterator.");

static_assert(
!IsContiguousIterator<std::vector<bool>::const_reverse_iterator>::value,
"Error: std::vector<bool>::const_reverse_iterator should not be "
"considered a contiguous iterator.");
}

TEST(ContiguousIterator, Array) {
static_assert(IsContiguousIterator<std::array<int, 1>::iterator>::value,
"Error: std::array<int, 1>::iterator should be considered a "
"contiguous iterator.");

static_assert(IsContiguousIterator<std::array<int, 1>::const_iterator>::value,
"Error: std::array<int, 1>::const_iterator should be "
"considered a contiguous iterator.");

static_assert(
!IsContiguousIterator<std::array<int, 1>::reverse_iterator>::value,
"Error: std::array<int, 1>::reverse_iterator should not be considered "
"a contiguous iterator.");

static_assert(
!IsContiguousIterator<std::array<int, 1>::const_reverse_iterator>::value,
"Error: std::array<int, 1>::const_reverse_iterator should not be "
"considered a contiguous iterator.");
}

TEST(ContiguousIterator, String) {
static_assert(IsContiguousIterator<std::string::iterator>::value,
"Error: std::string:iterator should be considered a contiguous"
"iterator.");

static_assert(IsContiguousIterator<std::string::const_iterator>::value,
"Error: std::string::const_iterator should be considered a "
"contiguous iterator.");

static_assert(!IsContiguousIterator<std::string::reverse_iterator>::value,
"Error: std::string::reverse_iterator should not be considered "
"a contiguous iterator.");

static_assert(
!IsContiguousIterator<std::string::const_reverse_iterator>::value,
"Error: std::string::const_reverse_iterator should not be considered "
"a contiguous iterator.");
}

TEST(ContiguousIterator, String16) {
static_assert(IsContiguousIterator<base::string16::iterator>::value,
"Error: base::string16:iterator should be considered a "
"contiguous iterator.");

static_assert(IsContiguousIterator<base::string16::const_iterator>::value,
"Error: base::string16::const_iterator should be considered a "
"contiguous iterator.");

static_assert(!IsContiguousIterator<base::string16::reverse_iterator>::value,
"Error: base::string16::reverse_iterator should not be "
"considered a contiguous iterator.");

static_assert(
!IsContiguousIterator<base::string16::const_reverse_iterator>::value,
"Error: base::string16::const_reverse_iterator should not be considered a"
"contiguous iterator.");
}

TEST(ContiguousIterator, Valarray) {
static_assert(IsContiguousIterator<decltype(
std::begin(std::declval<std::valarray<int>&>()))>::value,
"Error: std::valarray<int>::iterator should be considered a "
"contiguous iterator.");

static_assert(
IsContiguousIterator<decltype(
std::begin(std::declval<const std::valarray<int>&>()))>::value,
"Error: std::valarray<int>::const_iterator should be considered a "
"contiguous iterator.");
}

TEST(ContiguousIterator, StringPiece) {
static_assert(
IsContiguousIterator<base::StringPiece::const_iterator>::value,
"Error: base::StringPiece::const_iterator should be considered a "
"contiguous iterator.");

static_assert(
!IsContiguousIterator<base::StringPiece::const_reverse_iterator>::value,
"Error: base::StringPiece::const_reverse_iterator should not be "
"considered a contiguous iterator.");
}

TEST(ContiguousIterator, Span) {
static_assert(IsContiguousIterator<base::span<int>::iterator>::value,
"Error: base::span<int>::iterator should be considered a "
"contiguous iterator.");

static_assert(!IsContiguousIterator<base::span<int>::reverse_iterator>::value,
"Error: base::span<int>::reverse_iterator should not be "
"considered a contiguous iterator.");
}

} // namespace base
Loading

0 comments on commit 9269f9e

Please sign in to comment.