Skip to content

Commit 9e764b4

Browse files
Quincunx271copybara-github
authored andcommitted
Factor out some iterator traits detection code
There are a few different cases where we check iterator categorization, mostly for forward iterators for preallocating buffers of the correct size. Factoring this out makes it easier to make all of these cases support the C++20 iterator model. PiperOrigin-RevId: 725791190 Change-Id: Icf9d687654618c7ceff98ec76ec59e83c682dd6b
1 parent 05e72a3 commit 9e764b4

13 files changed

+184
-15
lines changed

CMake/AbseilDll.cmake

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ set(ABSL_INTERNAL_DLL_FILES
2121
"base/internal/fast_type_id.h"
2222
"base/internal/hide_ptr.h"
2323
"base/internal/identity.h"
24+
"base/internal/iterator_traits.h"
2425
"base/internal/invoke.h"
2526
"base/internal/inline_variable.h"
2627
"base/internal/low_level_alloc.cc"

absl/base/BUILD.bazel

+21
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,27 @@ cc_test(
955955
],
956956
)
957957

958+
cc_library(
959+
name = "iterator_traits_internal",
960+
hdrs = ["internal/iterator_traits.h"],
961+
copts = ABSL_DEFAULT_COPTS,
962+
linkopts = ABSL_DEFAULT_LINKOPTS,
963+
deps = [":config"],
964+
)
965+
966+
cc_test(
967+
name = "iterator_traits_test",
968+
srcs = ["internal/iterator_traits_test.cc"],
969+
copts = ABSL_TEST_COPTS,
970+
linkopts = ABSL_DEFAULT_LINKOPTS,
971+
deps = [
972+
":config",
973+
":iterator_traits_internal",
974+
"@googletest//:gtest",
975+
"@googletest//:gtest_main",
976+
],
977+
)
978+
958979
cc_library(
959980
name = "tracing_internal",
960981
srcs = ["internal/tracing.cc"],

absl/base/CMakeLists.txt

+26
Original file line numberDiff line numberDiff line change
@@ -834,3 +834,29 @@ absl_cc_test(
834834
absl::tracing_internal
835835
GTest::gtest_main
836836
)
837+
838+
# Internal-only target, do not depend on directly.
839+
absl_cc_library(
840+
NAME
841+
iterator_traits_internal
842+
HDRS
843+
"internal/iterator_traits.h"
844+
COPTS
845+
${ABSL_DEFAULT_COPTS}
846+
DEPS
847+
absl::config
848+
PUBLIC
849+
)
850+
851+
absl_cc_test(
852+
NAME
853+
iterator_traits_test
854+
SRCS
855+
"internal/iterator_traits_test.cc"
856+
COPTS
857+
${ABSL_TEST_COPTS}
858+
DEPS
859+
absl::config
860+
absl::iterator_traits_internal
861+
GTest::gtest_main
862+
)

absl/base/internal/iterator_traits.h

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2025 The Abseil Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// -----------------------------------------------------------------------------
16+
// File: internal/iterator_traits.h
17+
// -----------------------------------------------------------------------------
18+
//
19+
// Helpers for querying traits of iterators, for implementing containers, etc.
20+
21+
#ifndef ABSL_BASE_INTERNAL_ITERATOR_TRAITS_H_
22+
#define ABSL_BASE_INTERNAL_ITERATOR_TRAITS_H_
23+
24+
#include <iterator>
25+
#include <type_traits>
26+
27+
#include "absl/base/config.h"
28+
29+
namespace absl {
30+
ABSL_NAMESPACE_BEGIN
31+
namespace base_internal {
32+
33+
template <typename IteratorTag, typename Iterator>
34+
using IsAtLeastIterator = std::is_convertible<
35+
typename std::iterator_traits<Iterator>::iterator_category, IteratorTag>;
36+
37+
template <typename Iterator>
38+
using IsAtLeastForwardIterator =
39+
IsAtLeastIterator<std::forward_iterator_tag, Iterator>;
40+
41+
} // namespace base_internal
42+
ABSL_NAMESPACE_END
43+
} // namespace absl
44+
45+
#endif // ABSL_BASE_INTERNAL_ITERATOR_TRAITS_H_
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2025 The Abseil Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "absl/base/internal/iterator_traits.h"
16+
17+
#include <forward_list>
18+
#include <iterator>
19+
#include <list>
20+
#include <vector>
21+
22+
#include "gtest/gtest.h"
23+
#include "absl/base/config.h"
24+
25+
namespace absl {
26+
ABSL_NAMESPACE_BEGIN
27+
namespace base_internal {
28+
namespace {
29+
30+
TEST(IsAtLeastIteratorTest, IsAtLeastIterator) {
31+
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag, int*>()));
32+
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag, int*>()));
33+
EXPECT_TRUE((IsAtLeastIterator<std::bidirectional_iterator_tag, int*>()));
34+
EXPECT_TRUE((IsAtLeastIterator<std::random_access_iterator_tag, int*>()));
35+
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag,
36+
std::vector<int>::iterator>()));
37+
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag,
38+
std::vector<int>::iterator>()));
39+
EXPECT_TRUE((IsAtLeastIterator<std::bidirectional_iterator_tag,
40+
std::vector<int>::iterator>()));
41+
EXPECT_TRUE((IsAtLeastIterator<std::random_access_iterator_tag,
42+
std::vector<int>::iterator>()));
43+
44+
EXPECT_TRUE(
45+
(IsAtLeastIterator<std::input_iterator_tag, std::list<int>::iterator>()));
46+
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag,
47+
std::list<int>::iterator>()));
48+
EXPECT_TRUE((IsAtLeastIterator<std::bidirectional_iterator_tag,
49+
std::list<int>::iterator>()));
50+
EXPECT_FALSE((IsAtLeastIterator<std::random_access_iterator_tag,
51+
std::list<int>::iterator>()));
52+
53+
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag,
54+
std::forward_list<int>::iterator>()));
55+
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag,
56+
std::forward_list<int>::iterator>()));
57+
EXPECT_FALSE((IsAtLeastIterator<std::bidirectional_iterator_tag,
58+
std::forward_list<int>::iterator>()));
59+
EXPECT_FALSE((IsAtLeastIterator<std::random_access_iterator_tag,
60+
std::forward_list<int>::iterator>()));
61+
62+
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag,
63+
std::istream_iterator<int>>()));
64+
EXPECT_FALSE((IsAtLeastIterator<std::forward_iterator_tag,
65+
std::istream_iterator<int>>()));
66+
EXPECT_FALSE((IsAtLeastIterator<std::bidirectional_iterator_tag,
67+
std::istream_iterator<int>>()));
68+
EXPECT_FALSE((IsAtLeastIterator<std::random_access_iterator_tag,
69+
std::istream_iterator<int>>()));
70+
}
71+
72+
} // namespace
73+
} // namespace base_internal
74+
ABSL_NAMESPACE_END
75+
} // namespace absl

absl/container/BUILD.bazel

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ cc_library(
7070
"//absl/base:config",
7171
"//absl/base:core_headers",
7272
"//absl/base:dynamic_annotations",
73+
"//absl/base:iterator_traits_internal",
7374
"//absl/base:throw_delegate",
7475
"//absl/memory",
7576
],
@@ -144,6 +145,7 @@ cc_library(
144145
":inlined_vector_internal",
145146
"//absl/algorithm",
146147
"//absl/base:core_headers",
148+
"//absl/base:iterator_traits_internal",
147149
"//absl/base:throw_delegate",
148150
"//absl/memory",
149151
"//absl/meta:type_traits",

absl/container/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ absl_cc_library(
131131
absl::config
132132
absl::core_headers
133133
absl::dynamic_annotations
134+
absl::iterator_traits_internal
134135
absl::throw_delegate
135136
absl::memory
136137
PUBLIC

absl/container/fixed_array.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "absl/base/attributes.h"
4545
#include "absl/base/config.h"
4646
#include "absl/base/dynamic_annotations.h"
47+
#include "absl/base/internal/iterator_traits.h"
4748
#include "absl/base/internal/throw_delegate.h"
4849
#include "absl/base/macros.h"
4950
#include "absl/base/optimization.h"
@@ -85,9 +86,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray {
8586
// std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
8687
// but this seems to be mostly pedantic.
8788
template <typename Iterator>
88-
using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible<
89-
typename std::iterator_traits<Iterator>::iterator_category,
90-
std::forward_iterator_tag>::value>;
89+
using EnableIfForwardIterator = std::enable_if_t<
90+
base_internal::IsAtLeastForwardIterator<Iterator>::value>;
9191
static constexpr bool NoexceptCopyable() {
9292
return std::is_nothrow_copy_constructible<StorageElement>::value &&
9393
absl::allocator_is_nothrow<allocator_type>::value;

absl/container/inlined_vector.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
#include "absl/algorithm/algorithm.h"
4949
#include "absl/base/attributes.h"
50+
#include "absl/base/internal/iterator_traits.h"
5051
#include "absl/base/internal/throw_delegate.h"
5152
#include "absl/base/macros.h"
5253
#include "absl/base/optimization.h"
@@ -90,11 +91,11 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector {
9091
inlined_vector_internal::DefaultValueAdapter<TheA>;
9192

9293
template <typename Iterator>
93-
using EnableIfAtLeastForwardIterator = absl::enable_if_t<
94-
inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
94+
using EnableIfAtLeastForwardIterator = std::enable_if_t<
95+
base_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
9596
template <typename Iterator>
96-
using DisableIfAtLeastForwardIterator = absl::enable_if_t<
97-
!inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
97+
using DisableIfAtLeastForwardIterator = std::enable_if_t<
98+
!base_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
9899

99100
using MemcpyPolicy = typename Storage::MemcpyPolicy;
100101
using ElementwiseAssignPolicy = typename Storage::ElementwiseAssignPolicy;

absl/container/internal/inlined_vector.h

-5
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,6 @@ using ConstReverseIterator = typename std::reverse_iterator<ConstIterator<A>>;
7373
template <typename A>
7474
using MoveIterator = typename std::move_iterator<Iterator<A>>;
7575

76-
template <typename Iterator>
77-
using IsAtLeastForwardIterator = std::is_convertible<
78-
typename std::iterator_traits<Iterator>::iterator_category,
79-
std::forward_iterator_tag>;
80-
8176
template <typename A>
8277
using IsMoveAssignOk = std::is_move_assignable<ValueType<A>>;
8378
template <typename A>

absl/strings/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ cc_library(
104104
"//absl/base:config",
105105
"//absl/base:core_headers",
106106
"//absl/base:endian",
107+
"//absl/base:iterator_traits_internal",
107108
"//absl/base:nullability",
108109
"//absl/base:raw_logging_internal",
109110
"//absl/base:throw_delegate",

absl/strings/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ absl_cc_library(
8484
absl::core_headers
8585
absl::endian
8686
absl::int128
87+
absl::iterator_traits_internal
8788
absl::memory
8889
absl::nullability
8990
absl::raw_logging_internal

absl/strings/internal/str_join_internal.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <utility>
4444

4545
#include "absl/base/config.h"
46+
#include "absl/base/internal/iterator_traits.h"
4647
#include "absl/base/internal/raw_logging.h"
4748
#include "absl/strings/internal/ostringstream.h"
4849
#include "absl/strings/internal/resize_uninitialized.h"
@@ -228,9 +229,8 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
228229
// range will be traversed twice: once to calculate the total needed size, and
229230
// then again to copy the elements and delimiters to the output string.
230231
template <typename Iterator,
231-
typename = typename std::enable_if<std::is_convertible<
232-
typename std::iterator_traits<Iterator>::iterator_category,
233-
std::forward_iterator_tag>::value>::type>
232+
typename = std::enable_if_t<
233+
base_internal::IsAtLeastForwardIterator<Iterator>::value>>
234234
std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
235235
NoFormatter) {
236236
std::string result;

0 commit comments

Comments
 (0)