[libcxx-commits] [libcxx] 21f4232 - [libc++] Enable segmented iterator optimizations for join_view::iterator
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jan 19 22:56:04 PST 2023
Author: Nikolas Klauser
Date: 2023-01-20T07:55:58+01:00
New Revision: 21f4232dd963c449231f03a90836071202fd134a
URL: https://github.com/llvm/llvm-project/commit/21f4232dd963c449231f03a90836071202fd134a
DIFF: https://github.com/llvm/llvm-project/commit/21f4232dd963c449231f03a90836071202fd134a.diff
LOG: [libc++] Enable segmented iterator optimizations for join_view::iterator
Reviewed By: ldionne, #libc
Spies: libcxx-commits
Differential Revision: https://reviews.llvm.org/D138413
Added:
libcxx/benchmarks/join_view.bench.cpp
libcxx/include/__iterator/iterator_with_data.h
libcxx/test/libcxx/iterators/iterator_with_data.pass.cpp
Modified:
libcxx/benchmarks/CMakeLists.txt
libcxx/docs/ReleaseNotes.rst
libcxx/include/CMakeLists.txt
libcxx/include/__ranges/join_view.h
libcxx/include/module.modulemap.in
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt
index fbc5144db3219..7eb76ac6370fb 100644
--- a/libcxx/benchmarks/CMakeLists.txt
+++ b/libcxx/benchmarks/CMakeLists.txt
@@ -185,6 +185,7 @@ set(BENCHMARK_TESTS
formatter_float.bench.cpp
formatter_int.bench.cpp
function.bench.cpp
+ join_view.bench.cpp
map.bench.cpp
monotonic_buffer.bench.cpp
ordered_set.bench.cpp
diff --git a/libcxx/benchmarks/join_view.bench.cpp b/libcxx/benchmarks/join_view.bench.cpp
new file mode 100644
index 0000000000000..c789a39a8e208
--- /dev/null
+++ b/libcxx/benchmarks/join_view.bench.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <deque>
+#include <ranges>
+
+#include "benchmark/benchmark.h"
+
+namespace {
+void run_sizes(auto benchmark) {
+ benchmark->Arg(0)
+ ->Arg(1)
+ ->Arg(2)
+ ->Arg(64)
+ ->Arg(512)
+ ->Arg(1024)
+ ->Arg(4000)
+ ->Arg(4096)
+ ->Arg(5500)
+ ->Arg(64000)
+ ->Arg(65536)
+ ->Arg(70000);
+}
+
+void BM_join_view_in_vectors(benchmark::State& state) {
+ auto size = state.range(0);
+ std::vector<std::vector<int>> input(size, std::vector<int>(32));
+ std::ranges::fill(input | std::views::join, 10);
+ std::vector<int> output;
+ output.resize(size * 32);
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(input);
+ benchmark::DoNotOptimize(output);
+ std::ranges::copy(input | std::views::join, output.begin());
+ }
+}
+BENCHMARK(BM_join_view_in_vectors)->Apply(run_sizes);
+
+void BM_join_view_out_vectors(benchmark::State& state) {
+ auto size = state.range(0);
+ std::vector<std::vector<int>> output(size, std::vector<int>(32));
+ std::vector<int> input;
+ input.resize(size * 32);
+ std::ranges::fill(input, 10);
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(output);
+ benchmark::DoNotOptimize(input);
+ std::ranges::copy(input, (output | std::views::join).begin());
+ }
+}
+BENCHMARK(BM_join_view_out_vectors)->Apply(run_sizes);
+
+void BM_join_view_deques(benchmark::State& state) {
+ auto size = state.range(0);
+ std::deque<std::deque<int>> deque(size, std::deque<int>(32));
+ std::ranges::fill(deque | std::views::join, 10);
+ std::vector<int> output;
+ output.resize(size * 32);
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(deque);
+ benchmark::DoNotOptimize(output);
+ std::ranges::copy(deque | std::views::join, output.begin());
+ }
+}
+BENCHMARK(BM_join_view_deques)->Apply(run_sizes);
+} // namespace
+
+BENCHMARK_MAIN();
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 0d5e95e5040c2..c0e55424294ff 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -79,6 +79,9 @@ Improvements and New Features
- `D122780 <https://reviews.llvm.org/D122780>`_ Improved the performance of std::sort
- The ``ranges`` versions of ``copy``, ``move``, ``copy_backward`` and ``move_backward`` are now also optimized for
``std::deque<>::iterator``, which can lead to up to 20x performance improvements on certain algorithms.
+- The ``std`` and ``ranges`` versions of ``copy``, ``move``, ``copy_backward`` and ``move_backward`` are now also
+ optimized for ``join_view::iterator``, which can lead to up to 20x performance improvements on certain combinations of
+ iterators and algorithms.
Deprecations and Removals
-------------------------
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 74d1f96326e9e..44e129aeb391c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -392,6 +392,7 @@ set(files
__iterator/iter_swap.h
__iterator/iterator.h
__iterator/iterator_traits.h
+ __iterator/iterator_with_data.h
__iterator/mergeable.h
__iterator/move_iterator.h
__iterator/move_sentinel.h
diff --git a/libcxx/include/__iterator/iterator_with_data.h b/libcxx/include/__iterator/iterator_with_data.h
new file mode 100644
index 0000000000000..06c2fa699c30e
--- /dev/null
+++ b/libcxx/include/__iterator/iterator_with_data.h
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ITERATOR_ITERATOR_WITH_DATA_H
+#define _LIBCPP___ITERATOR_ITERATOR_WITH_DATA_H
+
+#include <__compare/compare_three_way_result.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/readable_traits.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 20
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <forward_iterator _Iterator, class _Data>
+class __iterator_with_data {
+ _Iterator __iter_{};
+ _Data __data_{};
+
+public:
+ using value_type = iter_value_t<_Iterator>;
+ using
diff erence_type = iter_
diff erence_t<_Iterator>;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator_with_data() = default;
+
+ constexpr _LIBCPP_HIDE_FROM_ABI __iterator_with_data(_Iterator __iter, _Data __data)
+ : __iter_(std::move(__iter)), __data_(std::move(__data)) {}
+
+ constexpr _LIBCPP_HIDE_FROM_ABI _Iterator __get_iter() const { return __iter_; }
+
+ constexpr _LIBCPP_HIDE_FROM_ABI _Data __get_data() && { return std::move(__data_); }
+
+ friend constexpr _LIBCPP_HIDE_FROM_ABI bool
+ operator==(const __iterator_with_data& __lhs, const __iterator_with_data& __rhs) {
+ return __lhs.__iter_ == __rhs.__iter_;
+ }
+
+ constexpr _LIBCPP_HIDE_FROM_ABI __iterator_with_data& operator++() {
+ ++__iter_;
+ return *this;
+ }
+
+ constexpr _LIBCPP_HIDE_FROM_ABI __iterator_with_data operator++(int) {
+ auto __tmp = *this;
+ __iter_++;
+ return __tmp;
+ }
+
+ constexpr _LIBCPP_HIDE_FROM_ABI __iterator_with_data& operator--()
+ requires bidirectional_iterator<_Iterator>
+ {
+ --__iter_;
+ return *this;
+ }
+
+ constexpr _LIBCPP_HIDE_FROM_ABI __iterator_with_data operator--(int)
+ requires bidirectional_iterator<_Iterator>
+ {
+ auto __tmp = *this;
+ --__iter_;
+ return __tmp;
+ }
+
+ constexpr _LIBCPP_HIDE_FROM_ABI iter_reference_t<_Iterator> operator*() const { return *__iter_; }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr iter_rvalue_reference_t<_Iterator>
+ iter_move(const __iterator_with_data& __iter) noexcept(noexcept(ranges::iter_move(__iter.__iter_))) {
+ return ranges::iter_move(__iter.__iter_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr void
+ iter_swap(const __iterator_with_data& __lhs,
+ const __iterator_with_data& __rhs) noexcept(noexcept(ranges::iter_swap(__lhs.__iter_, __rhs.__iter_)))
+ requires indirectly_swappable<_Iterator>
+ {
+ return ranges::iter_swap(__lhs.__data_, __rhs.__iter_);
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 20
+
+#endif // _LIBCPP___ITERATOR_ITERATOR_WITH_DATA_H
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 293926cc1264e..869540fc99c21 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -20,9 +20,12 @@
#include <__iterator/iter_move.h>
#include <__iterator/iter_swap.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/iterator_with_data.h>
+#include <__iterator/segmented_iterator.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
+#include <__ranges/empty.h>
#include <__ranges/non_propagating_cache.h>
#include <__ranges/range_adaptor.h>
#include <__ranges/view_interface.h>
@@ -63,6 +66,14 @@ namespace ranges {
>;
};
+ template <input_range _View, bool _Const>
+ requires view<_View> && input_range<range_reference_t<_View>>
+ struct __join_view_iterator;
+
+ template <input_range _View, bool _Const>
+ requires view<_View> && input_range<range_reference_t<_View>>
+ struct __join_view_sentinel;
+
template<input_range _View>
requires view<_View> && input_range<range_reference_t<_View>>
class join_view
@@ -70,8 +81,22 @@ namespace ranges {
private:
using _InnerRange = range_reference_t<_View>;
- template<bool> struct __iterator;
- template<bool> struct __sentinel;
+ template<bool _Const>
+ using __iterator = __join_view_iterator<_View, _Const>;
+
+ template<bool _Const>
+ using __sentinel = __join_view_sentinel<_View, _Const>;
+
+ template <input_range _View2, bool _Const2>
+ requires view<_View2> && input_range<range_reference_t<_View2>>
+ friend struct __join_view_iterator;
+
+ template <input_range _View2, bool _Const2>
+ requires view<_View2> && input_range<range_reference_t<_View2>>
+ friend struct __join_view_sentinel;
+
+ template <class>
+ friend struct std::__segmented_iterator_traits;
static constexpr bool _UseCache = !is_reference_v<_InnerRange>;
using _Cache = _If<_UseCache, __non_propagating_cache<remove_cvref_t<_InnerRange>>, __empty_cache>;
@@ -139,49 +164,57 @@ namespace ranges {
}
};
- template<input_range _View>
+ template<input_range _View, bool _Const>
requires view<_View> && input_range<range_reference_t<_View>>
- template<bool _Const> struct join_view<_View>::__sentinel {
- template<bool> friend struct __sentinel;
+ struct __join_view_sentinel {
+ template<input_range _View2, bool>
+ requires view<_View2> && input_range<range_reference_t<_View2>>
+ friend struct __join_view_sentinel;
private:
- using _Parent = __maybe_const<_Const, join_view>;
+ using _Parent = __maybe_const<_Const, join_view<_View>>;
using _Base = __maybe_const<_Const, _View>;
sentinel_t<_Base> __end_ = sentinel_t<_Base>();
public:
_LIBCPP_HIDE_FROM_ABI
- __sentinel() = default;
+ __join_view_sentinel() = default;
_LIBCPP_HIDE_FROM_ABI
- constexpr explicit __sentinel(_Parent& __parent)
+ constexpr explicit __join_view_sentinel(_Parent& __parent)
: __end_(ranges::end(__parent.__base_)) {}
_LIBCPP_HIDE_FROM_ABI
- constexpr __sentinel(__sentinel<!_Const> __s)
+ constexpr __join_view_sentinel(__join_view_sentinel<_View, !_Const> __s)
requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
: __end_(std::move(__s.__end_)) {}
template<bool _OtherConst>
requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
_LIBCPP_HIDE_FROM_ABI
- friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+ friend constexpr bool operator==(const __join_view_iterator<_View, _OtherConst>& __x, const __join_view_sentinel& __y) {
return __x.__outer_ == __y.__end_;
}
};
- template<input_range _View>
+ template<input_range _View, bool _Const>
requires view<_View> && input_range<range_reference_t<_View>>
- template<bool _Const> struct join_view<_View>::__iterator
+ struct __join_view_iterator
: public __join_view_iterator_category<__maybe_const<_Const, _View>> {
- template<bool> friend struct __iterator;
+ template<input_range _View2, bool>
+ requires view<_View2> && input_range<range_reference_t<_View2>>
+ friend struct __join_view_iterator;
+
+ template <class>
+ friend struct std::__segmented_iterator_traits;
private:
- using _Parent = __maybe_const<_Const, join_view>;
+ using _Parent = __maybe_const<_Const, join_view<_View>>;
using _Base = __maybe_const<_Const, _View>;
using _Outer = iterator_t<_Base>;
using _Inner = iterator_t<range_reference_t<_Base>>;
+ using _InnerRange = range_reference_t<_View>;
static constexpr bool __ref_is_glvalue = is_reference_v<range_reference_t<_Base>>;
@@ -210,6 +243,9 @@ namespace ranges {
__inner_.reset();
}
+ _LIBCPP_HIDE_FROM_ABI constexpr __join_view_iterator(_Parent* __parent, _Outer __outer, _Inner __inner)
+ : __outer_(std::move(__outer)), __inner_(std::move(__inner)), __parent_(__parent) {}
+
public:
using iterator_concept = _If<
__ref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<range_reference_t<_Base>> &&
@@ -228,17 +264,17 @@ namespace ranges {
range_
diff erence_t<_Base>, range_
diff erence_t<range_reference_t<_Base>>>;
_LIBCPP_HIDE_FROM_ABI
- __iterator() requires default_initializable<_Outer> = default;
+ __join_view_iterator() requires default_initializable<_Outer> = default;
_LIBCPP_HIDE_FROM_ABI
- constexpr __iterator(_Parent& __parent, _Outer __outer)
+ constexpr __join_view_iterator(_Parent& __parent, _Outer __outer)
: __outer_(std::move(__outer))
, __parent_(std::addressof(__parent)) {
__satisfy();
}
_LIBCPP_HIDE_FROM_ABI
- constexpr __iterator(__iterator<!_Const> __i)
+ constexpr __join_view_iterator(__join_view_iterator<_View, !_Const> __i)
requires _Const &&
convertible_to<iterator_t<_View>, _Outer> &&
convertible_to<iterator_t<_InnerRange>, _Inner>
@@ -259,7 +295,7 @@ namespace ranges {
}
_LIBCPP_HIDE_FROM_ABI
- constexpr __iterator& operator++() {
+ constexpr __join_view_iterator& operator++() {
auto&& __inner = [&]() -> auto&& {
if constexpr (__ref_is_glvalue)
return *__outer_;
@@ -279,7 +315,7 @@ namespace ranges {
}
_LIBCPP_HIDE_FROM_ABI
- constexpr __iterator operator++(int)
+ constexpr __join_view_iterator operator++(int)
requires __ref_is_glvalue &&
forward_range<_Base> &&
forward_range<range_reference_t<_Base>>
@@ -290,7 +326,7 @@ namespace ranges {
}
_LIBCPP_HIDE_FROM_ABI
- constexpr __iterator& operator--()
+ constexpr __join_view_iterator& operator--()
requires __ref_is_glvalue &&
bidirectional_range<_Base> &&
bidirectional_range<range_reference_t<_Base>> &&
@@ -309,7 +345,7 @@ namespace ranges {
}
_LIBCPP_HIDE_FROM_ABI
- constexpr __iterator operator--(int)
+ constexpr __join_view_iterator operator--(int)
requires __ref_is_glvalue &&
bidirectional_range<_Base> &&
bidirectional_range<range_reference_t<_Base>> &&
@@ -321,7 +357,7 @@ namespace ranges {
}
_LIBCPP_HIDE_FROM_ABI
- friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
+ friend constexpr bool operator==(const __join_view_iterator& __x, const __join_view_iterator& __y)
requires __ref_is_glvalue &&
equality_comparable<iterator_t<_Base>> &&
equality_comparable<iterator_t<range_reference_t<_Base>>>
@@ -330,14 +366,14 @@ namespace ranges {
}
_LIBCPP_HIDE_FROM_ABI
- friend constexpr decltype(auto) iter_move(const __iterator& __i)
+ friend constexpr decltype(auto) iter_move(const __join_view_iterator& __i)
noexcept(noexcept(ranges::iter_move(*__i.__inner_)))
{
return ranges::iter_move(*__i.__inner_);
}
_LIBCPP_HIDE_FROM_ABI
- friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y)
+ friend constexpr void iter_swap(const __join_view_iterator& __x, const __join_view_iterator& __y)
noexcept(noexcept(ranges::iter_swap(*__x.__inner_, *__y.__inner_)))
requires indirectly_swappable<_Inner>
{
@@ -365,6 +401,50 @@ inline namespace __cpo {
} // namespace views
} // namespace ranges
+template <class _View, bool _Const>
+ requires(ranges::common_range<typename ranges::__join_view_iterator<_View, _Const>::_Parent> &&
+ __is_cpp17_random_access_iterator<typename ranges::__join_view_iterator<_View, _Const>::_Outer>::value &&
+ __is_cpp17_random_access_iterator<typename ranges::__join_view_iterator<_View, _Const>::_Inner>::value)
+struct __segmented_iterator_traits<ranges::__join_view_iterator<_View, _Const>> {
+ using _JoinViewIterator = ranges::__join_view_iterator<_View, _Const>;
+
+ using __segment_iterator =
+ _LIBCPP_NODEBUG __iterator_with_data<typename _JoinViewIterator::_Outer, typename _JoinViewIterator::_Parent*>;
+ using __local_iterator = typename _JoinViewIterator::_Inner;
+
+ // TODO: Would it make sense to enable the optimization for other iterator types?
+
+ static constexpr _LIBCPP_HIDE_FROM_ABI __segment_iterator __segment(_JoinViewIterator __iter) {
+ if (ranges::empty(__iter.__parent_->__base_))
+ return {};
+ if (!__iter.__inner_.has_value())
+ return __segment_iterator(--__iter.__outer_, __iter.__parent_);
+ return __segment_iterator(__iter.__outer_, __iter.__parent_);
+ }
+
+ static constexpr _LIBCPP_HIDE_FROM_ABI __local_iterator __local(_JoinViewIterator __iter) {
+ if (ranges::empty(__iter.__parent_->__base_))
+ return {};
+ if (!__iter.__inner_.has_value())
+ return ranges::end(*--__iter.__outer_);
+ return *__iter.__inner_;
+ }
+
+ static constexpr _LIBCPP_HIDE_FROM_ABI __local_iterator __begin(__segment_iterator __iter) {
+ return ranges::begin(*__iter.__get_iter());
+ }
+
+ static constexpr _LIBCPP_HIDE_FROM_ABI __local_iterator __end(__segment_iterator __iter) {
+ return ranges::end(*__iter.__get_iter());
+ }
+
+ static constexpr _LIBCPP_HIDE_FROM_ABI _JoinViewIterator
+ __compose(__segment_iterator __seg_iter, __local_iterator __local_iter) {
+ return _JoinViewIterator(
+ std::move(__seg_iter).__get_data(), std::move(__seg_iter).__get_iter(), std::move(__local_iter));
+ }
+};
+
#endif // _LIBCPP_STD_VER > 17
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index ffc8e27e86926..ab9a21398fa78 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -996,6 +996,7 @@ module std [system] {
module iter_swap { private header "__iterator/iter_swap.h" }
module iterator { private header "__iterator/iterator.h" }
module iterator_traits { private header "__iterator/iterator_traits.h" }
+ module iterator_with_data { private header "__iterator/iterator_with_data.h" }
module mergeable {
private header "__iterator/mergeable.h"
export functional.__functional.ranges_operations
diff --git a/libcxx/test/libcxx/iterators/iterator_with_data.pass.cpp b/libcxx/test/libcxx/iterators/iterator_with_data.pass.cpp
new file mode 100644
index 0000000000000..a4c2b088d065e
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/iterator_with_data.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include "test_macros.h"
+
+TEST_DIAGNOSTIC_PUSH
+TEST_CLANG_DIAGNOSTIC_IGNORED("-Wprivate-header")
+#include <__iterator/iterator_with_data.h>
+TEST_DIAGNOSTIC_POP
+
+#include "test_iterators.h"
+
+static_assert(std::forward_iterator<std::__iterator_with_data<forward_iterator<int*>, int>>);
+static_assert(std::bidirectional_iterator<std::__iterator_with_data<bidirectional_iterator<int*>, int>>);
+static_assert(std::bidirectional_iterator<std::__iterator_with_data<random_access_iterator<int*>, int>>);
+static_assert(std::bidirectional_iterator<std::__iterator_with_data<contiguous_iterator<int*>, int>>);
+
+constexpr bool test() {
+ {
+ std::__iterator_with_data<forward_iterator<int*>, int> iter(forward_iterator<int*>(nullptr), 3);
+ assert(iter == iter);
+ assert(iter.__get_iter() == forward_iterator<int*>(nullptr));
+ assert(std::move(iter).__get_data() == 3);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+}
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 86d93b66ff52b..d67705378adb1 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -423,6 +423,7 @@ END-SCRIPT
#include <__iterator/iter_swap.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/iter_swap.h'}}
#include <__iterator/iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/iterator.h'}}
#include <__iterator/iterator_traits.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/iterator_traits.h'}}
+#include <__iterator/iterator_with_data.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/iterator_with_data.h'}}
#include <__iterator/mergeable.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/mergeable.h'}}
#include <__iterator/move_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/move_iterator.h'}}
#include <__iterator/move_sentinel.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/move_sentinel.h'}}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
index e8f8301ac4d6a..5b8d8b803df30 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
@@ -9,11 +9,16 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17
#include <algorithm>
+#include <array>
#include <cassert>
#include <concepts>
#include <deque>
+#include <ranges>
#include <vector>
+#include "test_iterators.h"
+#include "type_algorithms.h"
+
template <class InContainer, class OutContainer>
constexpr void test_containers() {
using InIter = typename InContainer::iterator;
@@ -39,6 +44,52 @@ constexpr void test_containers() {
}
}
+template <class Iter, class Sent>
+constexpr void test_join_view() {
+ auto to_subranges = std::views::transform([](auto& vec) {
+ return std::ranges::subrange(Iter(vec.data()), Sent(Iter(vec.data() + vec.size())));
+ });
+
+ { // segmented -> contiguous
+ std::vector<std::vector<int>> vectors = {};
+ auto range = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+ std::array<int, 0> arr;
+
+ std::ranges::copy(subrange_vector | std::views::join, arr.begin());
+ assert(std::ranges::equal(arr, std::array<int, 0>{}));
+ }
+ { // segmented -> contiguous
+ std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+ auto range = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+ std::array<int, 10> arr;
+
+ std::ranges::copy(subrange_vector | std::views::join, arr.begin());
+ assert(std::ranges::equal(arr, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
+ }
+ { // contiguous -> segmented
+ std::vector<std::vector<int>> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}};
+ auto range = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+ std::array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+ std::ranges::copy(arr, (subrange_vector | std::views::join).begin());
+ assert(std::ranges::equal(subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
+ }
+ { // segmented -> segmented
+ std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+ auto range1 = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range1.begin(), range1.end());
+ std::vector<std::vector<int>> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}};
+ auto range2 = to_vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> to_subrange_vector(range2.begin(), range2.end());
+
+ std::ranges::copy(subrange_vector | std::views::join, (to_subrange_vector | std::views::join).begin());
+ assert(std::ranges::equal(to_subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
+ }
+}
+
int main(int, char**) {
if (!std::is_constant_evaluated()) {
test_containers<std::deque<int>, std::deque<int>>();
@@ -47,5 +98,11 @@ int main(int, char**) {
test_containers<std::vector<int>, std::vector<int>>();
}
+ meta::for_each(meta::forward_iterator_list<int*>{}, []<class Iter> {
+ test_join_view<Iter, Iter>();
+ test_join_view<Iter, sentinel_wrapper<Iter>>();
+ test_join_view<Iter, sized_sentinel<Iter>>();
+ });
+
return 0;
}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
index 3762948e4ed1f..036dfe570b3bd 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
@@ -123,17 +123,69 @@ constexpr void test_containers() {
}
}
+template <class Iter, class Sent>
+constexpr void test_join_view() {
+ auto to_subranges = std::views::transform([](auto& vec) {
+ return std::ranges::subrange(Iter(vec.begin()), Sent(Iter(vec.end())));
+ });
+
+ { // segmented -> contiguous
+ std::vector<std::vector<int>> vectors = {};
+ auto range = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+ std::array<int, 0> arr;
+
+ std::ranges::copy_backward(subrange_vector | std::views::join, arr.end());
+ assert(std::ranges::equal(arr, std::array<int, 0>{}));
+ }
+ { // segmented -> contiguous
+ std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+ auto range = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+ std::array<int, 10> arr;
+
+ std::ranges::copy_backward(subrange_vector | std::views::join, arr.end());
+ assert(std::ranges::equal(arr, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
+ }
+ { // contiguous -> segmented
+ std::vector<std::vector<int>> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}};
+ auto range = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+ std::array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+ std::ranges::copy_backward(arr, (subrange_vector | std::views::join).end());
+ assert(std::ranges::equal(subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
+ }
+ { // segmented -> segmented
+ std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+ auto range1 = vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range1.begin(), range1.end());
+ std::vector<std::vector<int>> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}};
+ auto range2 = to_vectors | to_subranges;
+ std::vector<std::ranges::subrange<Iter, Sent>> to_subrange_vector(range2.begin(), range2.end());
+
+ std::ranges::copy_backward(subrange_vector | std::views::join, (to_subrange_vector | std::views::join).end());
+ assert(std::ranges::equal(to_subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
+ }
+}
+
+template <class>
+constexpr bool is_proxy_iterator = false;
+
+template <class Iter>
+constexpr bool is_proxy_iterator<ProxyIterator<Iter>> = true;
+
template <template <class> class InIter, template <class> class OutIter>
constexpr void test_sentinels() {
test_iterators<InIter<int*>, OutIter<int*>, InIter<int*>>();
test_iterators<InIter<int*>, OutIter<int*>, sentinel_wrapper<InIter<int*>>>();
test_iterators<InIter<int*>, OutIter<int*>, sized_sentinel<InIter<int*>>>();
- if (!std::is_constant_evaluated()) {
- if constexpr (!std::is_same_v<InIter<int*>, contiguous_iterator<int*>> &&
- !std::is_same_v<OutIter<int*>, contiguous_iterator<int*>> &&
- !std::is_same_v<InIter<int*>, ContiguousProxyIterator<int*>> &&
- !std::is_same_v<OutIter<int*>, ContiguousProxyIterator<int*>>) {
+ if constexpr (!std::is_same_v<InIter<int*>, contiguous_iterator<int*>> &&
+ !std::is_same_v<OutIter<int*>, contiguous_iterator<int*>> &&
+ !std::is_same_v<InIter<int*>, ContiguousProxyIterator<int*>> &&
+ !std::is_same_v<OutIter<int*>, ContiguousProxyIterator<int*>>) {
+ if (!std::is_constant_evaluated()) {
test_containers<std::deque<int>,
std::deque<int>,
InIter<std::deque<int>::iterator>,
@@ -151,6 +203,8 @@ constexpr void test_sentinels() {
InIter<std::vector<int>::iterator>,
OutIter<std::vector<int>::iterator>>();
}
+ if constexpr (!is_proxy_iterator<InIter<int*>>)
+ test_join_view<InIter<std::vector<int>::iterator>, InIter<std::vector<int>::iterator>>();
}
}
More information about the libcxx-commits
mailing list