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
This commit is contained in:
Justin Bassett
2025-02-11 15:18:19 -08:00
committed by Copybara-Service
parent 05e72a3285
commit 9e764b4f25
13 changed files with 184 additions and 15 deletions

View File

@@ -21,6 +21,7 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/fast_type_id.h"
"base/internal/hide_ptr.h"
"base/internal/identity.h"
"base/internal/iterator_traits.h"
"base/internal/invoke.h"
"base/internal/inline_variable.h"
"base/internal/low_level_alloc.cc"

View File

@@ -955,6 +955,27 @@ cc_test(
],
)
cc_library(
name = "iterator_traits_internal",
hdrs = ["internal/iterator_traits.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [":config"],
)
cc_test(
name = "iterator_traits_test",
srcs = ["internal/iterator_traits_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
":iterator_traits_internal",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
)
cc_library(
name = "tracing_internal",
srcs = ["internal/tracing.cc"],

View File

@@ -834,3 +834,29 @@ absl_cc_test(
absl::tracing_internal
GTest::gtest_main
)
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
iterator_traits_internal
HDRS
"internal/iterator_traits.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::config
PUBLIC
)
absl_cc_test(
NAME
iterator_traits_test
SRCS
"internal/iterator_traits_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::config
absl::iterator_traits_internal
GTest::gtest_main
)

View File

@@ -0,0 +1,45 @@
// Copyright 2025 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: internal/iterator_traits.h
// -----------------------------------------------------------------------------
//
// Helpers for querying traits of iterators, for implementing containers, etc.
#ifndef ABSL_BASE_INTERNAL_ITERATOR_TRAITS_H_
#define ABSL_BASE_INTERNAL_ITERATOR_TRAITS_H_
#include <iterator>
#include <type_traits>
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
template <typename IteratorTag, typename Iterator>
using IsAtLeastIterator = std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category, IteratorTag>;
template <typename Iterator>
using IsAtLeastForwardIterator =
IsAtLeastIterator<std::forward_iterator_tag, Iterator>;
} // namespace base_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_ITERATOR_TRAITS_H_

View File

@@ -0,0 +1,75 @@
// Copyright 2025 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/base/internal/iterator_traits.h"
#include <forward_list>
#include <iterator>
#include <list>
#include <vector>
#include "gtest/gtest.h"
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
TEST(IsAtLeastIteratorTest, IsAtLeastIterator) {
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag, int*>()));
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag, int*>()));
EXPECT_TRUE((IsAtLeastIterator<std::bidirectional_iterator_tag, int*>()));
EXPECT_TRUE((IsAtLeastIterator<std::random_access_iterator_tag, int*>()));
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag,
std::vector<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag,
std::vector<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::bidirectional_iterator_tag,
std::vector<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::random_access_iterator_tag,
std::vector<int>::iterator>()));
EXPECT_TRUE(
(IsAtLeastIterator<std::input_iterator_tag, std::list<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag,
std::list<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::bidirectional_iterator_tag,
std::list<int>::iterator>()));
EXPECT_FALSE((IsAtLeastIterator<std::random_access_iterator_tag,
std::list<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag,
std::forward_list<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::forward_iterator_tag,
std::forward_list<int>::iterator>()));
EXPECT_FALSE((IsAtLeastIterator<std::bidirectional_iterator_tag,
std::forward_list<int>::iterator>()));
EXPECT_FALSE((IsAtLeastIterator<std::random_access_iterator_tag,
std::forward_list<int>::iterator>()));
EXPECT_TRUE((IsAtLeastIterator<std::input_iterator_tag,
std::istream_iterator<int>>()));
EXPECT_FALSE((IsAtLeastIterator<std::forward_iterator_tag,
std::istream_iterator<int>>()));
EXPECT_FALSE((IsAtLeastIterator<std::bidirectional_iterator_tag,
std::istream_iterator<int>>()));
EXPECT_FALSE((IsAtLeastIterator<std::random_access_iterator_tag,
std::istream_iterator<int>>()));
}
} // namespace
} // namespace base_internal
ABSL_NAMESPACE_END
} // namespace absl

View File

@@ -70,6 +70,7 @@ cc_library(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
"//absl/base:iterator_traits_internal",
"//absl/base:throw_delegate",
"//absl/memory",
],
@@ -144,6 +145,7 @@ cc_library(
":inlined_vector_internal",
"//absl/algorithm",
"//absl/base:core_headers",
"//absl/base:iterator_traits_internal",
"//absl/base:throw_delegate",
"//absl/memory",
"//absl/meta:type_traits",

View File

@@ -131,6 +131,7 @@ absl_cc_library(
absl::config
absl::core_headers
absl::dynamic_annotations
absl::iterator_traits_internal
absl::throw_delegate
absl::memory
PUBLIC

View File

@@ -44,6 +44,7 @@
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/dynamic_annotations.h"
#include "absl/base/internal/iterator_traits.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"
@@ -85,9 +86,8 @@ class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray {
// std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
// but this seems to be mostly pedantic.
template <typename Iterator>
using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
std::forward_iterator_tag>::value>;
using EnableIfForwardIterator = std::enable_if_t<
base_internal::IsAtLeastForwardIterator<Iterator>::value>;
static constexpr bool NoexceptCopyable() {
return std::is_nothrow_copy_constructible<StorageElement>::value &&
absl::allocator_is_nothrow<allocator_type>::value;

View File

@@ -47,6 +47,7 @@
#include "absl/algorithm/algorithm.h"
#include "absl/base/attributes.h"
#include "absl/base/internal/iterator_traits.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"
@@ -90,11 +91,11 @@ class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector {
inlined_vector_internal::DefaultValueAdapter<TheA>;
template <typename Iterator>
using EnableIfAtLeastForwardIterator = absl::enable_if_t<
inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
using EnableIfAtLeastForwardIterator = std::enable_if_t<
base_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
template <typename Iterator>
using DisableIfAtLeastForwardIterator = absl::enable_if_t<
!inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
using DisableIfAtLeastForwardIterator = std::enable_if_t<
!base_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
using MemcpyPolicy = typename Storage::MemcpyPolicy;
using ElementwiseAssignPolicy = typename Storage::ElementwiseAssignPolicy;

View File

@@ -73,11 +73,6 @@ using ConstReverseIterator = typename std::reverse_iterator<ConstIterator<A>>;
template <typename A>
using MoveIterator = typename std::move_iterator<Iterator<A>>;
template <typename Iterator>
using IsAtLeastForwardIterator = std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
std::forward_iterator_tag>;
template <typename A>
using IsMoveAssignOk = std::is_move_assignable<ValueType<A>>;
template <typename A>

View File

@@ -104,6 +104,7 @@ cc_library(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
"//absl/base:iterator_traits_internal",
"//absl/base:nullability",
"//absl/base:raw_logging_internal",
"//absl/base:throw_delegate",

View File

@@ -84,6 +84,7 @@ absl_cc_library(
absl::core_headers
absl::endian
absl::int128
absl::iterator_traits_internal
absl::memory
absl::nullability
absl::raw_logging_internal

View File

@@ -43,6 +43,7 @@
#include <utility>
#include "absl/base/config.h"
#include "absl/base/internal/iterator_traits.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/internal/ostringstream.h"
#include "absl/strings/internal/resize_uninitialized.h"
@@ -228,9 +229,8 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
// range will be traversed twice: once to calculate the total needed size, and
// then again to copy the elements and delimiters to the output string.
template <typename Iterator,
typename = typename std::enable_if<std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
std::forward_iterator_tag>::value>::type>
typename = std::enable_if_t<
base_internal::IsAtLeastForwardIterator<Iterator>::value>>
std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
NoFormatter) {
std::string result;