forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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: Daniel Cheng <dcheng@chromium.org> Cr-Commit-Position: refs/heads/master@{#816543}
- Loading branch information
Showing
9 changed files
with
480 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.