[libcxx-commits] [libcxx] [libc++] Implement ranges::iota (PR #68494)
James E T Smith via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Oct 7 11:51:54 PDT 2023
https://github.com/jamesETsmith created https://github.com/llvm/llvm-project/pull/68494
# Overview
As a disclaimer, this is my first PR to LLVM and while I've tried to ensure I've followed the LLVM and libc++ contributing guidelines, there's probably a good chance I missed something. If I have, just let me know and I'll try to correct it as soon as I can.
This PR implements `std::ranges::iota` and `std::ranges::out_value_result` outlined in [P2440r1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2440r1.html).
As outlined in the paper above, I've:
- Implemented `out_value_result` and added to `<algorithm>`
- Added `out_value_result`, `iota_result`, and two overloads of `iota` to `std::ranges` in `<numeric>`
- Updated the version macro `__cpp_lib_ranges_iota` in `<version>`
I've also added tests for `ranges::iota` and `ranges::out_value_result`. Lastly, I added those structs to the appropriate module files.
# TODOs
- [ ] Updating the range [status doc](https://github.com/jamesETsmith/llvm-project/blob/main/libcxx/docs/Status/RangesMajorFeatures.csv)
- [ ] I'm open to implementing the rest of P2440r1 (`ranges::shift_left` and `ranges::shift_right`) if that's ok, I just wanted to get feedback on `ranges::iota` first
- [ ] I've been having trouble building the modules locally and want to make sure that's working properly
>From c4a3ccfbad090ad8314aa8ad53092edc8d5432bc Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Thu, 28 Sep 2023 10:11:15 -0400
Subject: [PATCH 1/6] [libc++] Implement ranges::iota and
ranges::out_value_result
---
libcxx/include/CMakeLists.txt | 2 +
libcxx/include/__algorithm/out_value_result.h | 52 +++++++++
libcxx/include/__numeric/ranges_iota.h | 53 +++++++++
libcxx/include/algorithm | 4 +
libcxx/include/numeric | 1 +
libcxx/include/version | 2 +-
.../out_value_result.pass.cpp | 102 ++++++++++++++++++
.../numeric.iota/ranges.iota.pass.cpp | 52 +++++++++
8 files changed, 267 insertions(+), 1 deletion(-)
create mode 100644 libcxx/include/__algorithm/out_value_result.h
create mode 100644 libcxx/include/__numeric/ranges_iota.h
create mode 100644 libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2ec755236dbaee2..c6eb03f1d68e984 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -63,6 +63,7 @@ set(files
__algorithm/next_permutation.h
__algorithm/none_of.h
__algorithm/nth_element.h
+ __algorithm/out_value_result.h
__algorithm/partial_sort.h
__algorithm/partial_sort_copy.h
__algorithm/partition.h
@@ -561,6 +562,7 @@ set(files
__numeric/partial_sum.h
__numeric/pstl_reduce.h
__numeric/pstl_transform_reduce.h
+ __numeric/ranges_iota.h
__numeric/reduce.h
__numeric/transform_exclusive_scan.h
__numeric/transform_inclusive_scan.h
diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h
new file mode 100644
index 000000000000000..8baffec7b9ef4da
--- /dev/null
+++ b/libcxx/include/__algorithm/out_value_result.h
@@ -0,0 +1,52 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___ALGORITHM_OUT_VALUE_RESULT_H
+#define _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H
+
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _OutIter1, class _ValType1>
+struct out_value_result {
+ _LIBCPP_NO_UNIQUE_ADDRESS _OutIter1 out;
+ _LIBCPP_NO_UNIQUE_ADDRESS _ValType1 value;
+
+ template <class _OutIter2, class _ValType2>
+ requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
+ constexpr operator out_value_result<_OutIter2, _ValType2>() const& { return {out, value}; }
+
+ template <class _OutIter2, class _ValType2>
+ requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2>
+ constexpr operator out_value_result<_OutIter2, _ValType2>() && { return {std::move(out), std::move(value)}; }
+};
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
new file mode 100644
index 000000000000000..20311a68c2a348c
--- /dev/null
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -0,0 +1,53 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___NUMERIC_RANGES_IOTA_H
+#define _LIBCPP___NUMERIC_RANGES_IOTA_H
+
+#include <__algorithm/out_value_result.h>
+#include <__config>
+#include <__ranges/concepts.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+namespace ranges {
+template <typename _Out, typename _Tp>
+using iota_result = ranges::out_value_result<_Out, _Tp>;
+
+struct __iota_fn {
+ template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
+ requires indirectly_writable<_Out, const _Tp&>
+ constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
+ while (__first != __last) {
+ *__first = static_cast<const _Tp&>(__value);
+ ++__first;
+ ++__value;
+ }
+ return {std::move(__first), std::move(__value)};
+ }
+
+ template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range>
+ constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const {
+ return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value));
+ }
+};
+
+inline constexpr __iota_fn iota{};
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___NUMERIC_RANGES_IOTA_H
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 69ba9537dda6984..42f01a99a02cc93 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -42,6 +42,9 @@ namespace ranges {
template <class I>
struct in_found_result; // since C++20
+ template <class O, class T>
+ struct out_value_result; // since C++23
+
template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less> // since C++20
constexpr I min_element(I first, S last, Comp comp = {}, Proj proj = {});
@@ -1817,6 +1820,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/next_permutation.h>
#include <__algorithm/none_of.h>
#include <__algorithm/nth_element.h>
+#include <__algorithm/out_value_result.h>
#include <__algorithm/partial_sort.h>
#include <__algorithm/partial_sort_copy.h>
#include <__algorithm/partition.h>
diff --git a/libcxx/include/numeric b/libcxx/include/numeric
index 3fcf6cefdb4b884..363dfd229df54dc 100644
--- a/libcxx/include/numeric
+++ b/libcxx/include/numeric
@@ -160,6 +160,7 @@ template<class T>
#include <__numeric/partial_sum.h>
#include <__numeric/pstl_reduce.h>
#include <__numeric/pstl_transform_reduce.h>
+#include <__numeric/ranges_iota.h>
#include <__numeric/reduce.h>
#include <__numeric/transform_exclusive_scan.h>
#include <__numeric/transform_inclusive_scan.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index e5a995366a7aa48..164c60dfd6af6d4 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -436,7 +436,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# define __cpp_lib_ranges_as_rvalue 202207L
// # define __cpp_lib_ranges_chunk 202202L
# define __cpp_lib_ranges_chunk_by 202202L
-// # define __cpp_lib_ranges_iota 202202L
+# define __cpp_lib_ranges_iota 202202L
// # define __cpp_lib_ranges_join_with 202202L
# define __cpp_lib_ranges_repeat 202207L
// # define __cpp_lib_ranges_slide 202202L
diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
new file mode 100644
index 000000000000000..189149b0b4acb0c
--- /dev/null
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, C++20
+
+// template <class O, class T>
+// struct out_value_result;
+
+#include <algorithm>
+#include <cassert>
+#include <type_traits>
+
+#include "MoveOnly.h"
+
+struct A {
+ explicit A(int);
+};
+// no implicit conversion
+static_assert(!std::is_constructible_v<std::ranges::out_value_result<A, A>, std::ranges::out_value_result<int, int>>);
+
+struct B {
+ B(int);
+};
+// implicit conversion
+static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>>);
+static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>&>);
+static_assert(
+ std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>>);
+static_assert(
+ std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>&>);
+
+struct C {
+ C(int&);
+};
+static_assert(!std::is_constructible_v<std::ranges::out_value_result<C, C>, std::ranges::out_value_result<int, int>&>);
+
+// has to be convertible via const&
+static_assert(
+ std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
+static_assert(
+ std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
+static_assert(
+ std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
+static_assert(
+ std::is_convertible_v<const std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
+
+// should be move constructible
+static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>);
+static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>);
+
+struct NotConvertible {};
+// conversions should not work if there is no conversion
+static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>,
+ std::ranges::out_value_result<int, NotConvertible>>);
+static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
+ std::ranges::out_value_result<NotConvertible, int>>);
+
+template <class T>
+struct ConvertibleFrom {
+ constexpr ConvertibleFrom(T c) : content{c} {}
+ T content;
+};
+
+constexpr bool test() {
+ {
+ std::ranges::out_value_result<double, int> res{10, 1};
+ assert(res.out == 10);
+ assert(res.value == 1);
+ std::ranges::out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res;
+ assert(res2.out.content == 10);
+ assert(res2.value.content == 1);
+ }
+ {
+ std::ranges::out_value_result<MoveOnly, int> res{MoveOnly{}, 10};
+ assert(res.out.get() == 1);
+ assert(res.value == 10);
+ auto res2 = std::move(res);
+ assert(res.out.get() == 0);
+ assert(res.value == 10);
+ assert(res2.out.get() == 1);
+ assert(res2.value == 10);
+ }
+ {
+ auto [min, max] = std::ranges::out_value_result<int, int>{1, 2};
+ assert(min == 1);
+ assert(max == 2);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
new file mode 100644
index 000000000000000..1e2a6970adb35e3
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Testing std::ranges::iota
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <numeric>
+#include <array>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// This is pulled directly from the std::iota test
+template <class InIter>
+TEST_CONSTEXPR_CXX20 void test0() {
+ int ia[] = {1, 2, 3, 4, 5};
+ int ir[] = {5, 6, 7, 8, 9};
+ const unsigned s = sizeof(ia) / sizeof(ia[0]);
+ std::ranges::iota(InIter(ia), InIter(ia + s), 5);
+ for (unsigned i = 0; i < s; ++i)
+ assert(ia[i] == ir[i]);
+}
+
+TEST_CONSTEXPR_CXX20 bool test0() {
+ test0<forward_iterator<int*> >();
+ test0<bidirectional_iterator<int*> >();
+ test0<random_access_iterator<int*> >();
+ test0<int*>();
+
+ return true;
+}
+
+TEST_CONSTEXPR_CXX20 void test1() {
+ std::array<int, 5> ia = {1, 2, 3, 4, 5};
+ std::array<int, 5> ir = {5, 6, 7, 8, 9};
+ std::ranges::iota(ia, 5);
+ for (unsigned i = 0; i < ir.size(); ++i)
+ assert(ia[i] == ir[i]);
+}
+
+int main(int, char**) {
+ test0();
+ test1();
+ return 0;
+}
\ No newline at end of file
>From 574c94ce4f7eed06013a5e5d1460da1d148cc7cd Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Fri, 29 Sep 2023 14:11:49 -0400
Subject: [PATCH 2/6] [libc++] Implement ranges::iota: update module and docs
info
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/include/module.modulemap.in | 1 +
libcxx/modules/std/numeric.inc | 8 ++++++--
libcxx/utils/generate_feature_test_macro_components.py | 1 -
4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index e1b4172b22c53da..ebb18dc7bed9bd2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -348,7 +348,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_ranges_chunk_by`` ``202202L``
--------------------------------------------------- -----------------
- ``__cpp_lib_ranges_iota`` *unimplemented*
+ ``__cpp_lib_ranges_iota`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_join_with`` *unimplemented*
--------------------------------------------------- -----------------
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 6d9bb8653fcb5e9..b092cc7c0e93bf1 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1602,6 +1602,7 @@ module std_private_numeric_pstl_transform_reduce [system] {
export *
}
module std_private_numeric_reduce [system] { header "__numeric/reduce.h" }
+module std_private_numeric_ranges_iota [system] { header "__numeric/ranges_iota.h" }
module std_private_numeric_transform_exclusive_scan [system] { header "__numeric/transform_exclusive_scan.h" }
module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
module std_private_numeric_transform_reduce [system] { header "__numeric/transform_reduce.h" }
diff --git a/libcxx/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc
index d2b7688d4e5f10a..9204e8d8715935d 100644
--- a/libcxx/modules/std/numeric.inc
+++ b/libcxx/modules/std/numeric.inc
@@ -42,8 +42,12 @@ export namespace std {
using std::iota;
namespace ranges {
- // using std::ranges::iota_result;
- // using std::ranges::iota;
+
+#if _LIBCPP_STD_VER >= 23
+ using std::ranges::iota;
+ using std::ranges::iota_result;
+#endif // _LIBCPP_STD_VER >= 23
+
} // namespace ranges
// [numeric.ops.gcd], greatest common divisor
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index ac342aff0beb701..9fd50d66edbe317 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -806,7 +806,6 @@ def add_version_header(tc):
"name": "__cpp_lib_ranges_iota",
"values": {"c++23": 202202},
"headers": ["numeric"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_ranges_join_with",
>From e4cfe00cb1b348d5a3515ef907c7c87334c6824c Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Fri, 29 Sep 2023 16:50:46 -0400
Subject: [PATCH 3/6] [libc++] Implement ranges::iota: fixing formatting issues
for out_value_result
---
libcxx/include/__algorithm/out_value_result.h | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h
index 8baffec7b9ef4da..a99d206f7035ff2 100644
--- a/libcxx/include/__algorithm/out_value_result.h
+++ b/libcxx/include/__algorithm/out_value_result.h
@@ -34,11 +34,15 @@ struct out_value_result {
template <class _OutIter2, class _ValType2>
requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
- constexpr operator out_value_result<_OutIter2, _ValType2>() const& { return {out, value}; }
+ constexpr operator out_value_result<_OutIter2, _ValType2>() const& {
+ return {out, value};
+ }
template <class _OutIter2, class _ValType2>
requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2>
- constexpr operator out_value_result<_OutIter2, _ValType2>() && { return {std::move(out), std::move(value)}; }
+ constexpr operator out_value_result<_OutIter2, _ValType2>() && {
+ return {std::move(out), std::move(value)};
+ }
};
} // namespace ranges
>From 7cdfea2d5acf553345ee0c506ce9e3914cbb3ed0 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 7 Oct 2023 12:04:32 -0400
Subject: [PATCH 4/6] [libc++] Implement ranges::iota: Cleaning up tests for
ranges::iota and out_value_result. Updating Cxx23Papers.csv too.
---
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
libcxx/include/__numeric/ranges_iota.h | 3 +-
libcxx/modules/std/algorithm.inc | 2 +-
.../out_value_result.pass.cpp | 104 ++++++++++------
.../numeric.iota/ranges.iota.pass.cpp | 117 ++++++++++++++++--
5 files changed, 171 insertions(+), 57 deletions(-)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 9cb49fd5176ea5f..4618908cfb73941 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -46,7 +46,7 @@
"`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|"
-"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
+"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress|","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","","","|ranges|"
"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index 20311a68c2a348c..a8c069832da7e6d 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -13,6 +13,7 @@
#include <__algorithm/out_value_result.h>
#include <__config>
#include <__ranges/concepts.h>
+#include <__utility/as_const.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -30,7 +31,7 @@ struct __iota_fn {
requires indirectly_writable<_Out, const _Tp&>
constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
while (__first != __last) {
- *__first = static_cast<const _Tp&>(__value);
+ *__first = std::as_const(__value);
++__first;
++__value;
}
diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc
index b7900d15c10c2b5..62ce8cf02c14f95 100644
--- a/libcxx/modules/std/algorithm.inc
+++ b/libcxx/modules/std/algorithm.inc
@@ -18,7 +18,7 @@ export namespace std {
using std::ranges::in_out_result;
// using std::ranges::in_value_result;
using std::ranges::min_max_result;
- // using std::ranges::out_value_result;
+ using std::ranges::out_value_result;
} // namespace ranges
// [alg.nonmodifying], non-modifying sequence operations
diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
index 189149b0b4acb0c..3a930bb063f975f 100644
--- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -17,48 +17,25 @@
#include "MoveOnly.h"
-struct A {
- explicit A(int);
+//
+// Helper structs
+//
+
+// only explicit construction
+struct IterTypeExplicit {
+ explicit IterTypeExplicit(int*);
};
-// no implicit conversion
-static_assert(!std::is_constructible_v<std::ranges::out_value_result<A, A>, std::ranges::out_value_result<int, int>>);
-struct B {
- B(int);
+// implicit construction
+struct IterTypeImplicit {
+ IterTypeImplicit(int*);
};
-// implicit conversion
-static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>>);
-static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>&>);
-static_assert(
- std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>>);
-static_assert(
- std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>&>);
-
-struct C {
- C(int&);
+
+struct IterTypeImplicitRef {
+ IterTypeImplicitRef(int&);
};
-static_assert(!std::is_constructible_v<std::ranges::out_value_result<C, C>, std::ranges::out_value_result<int, int>&>);
-
-// has to be convertible via const&
-static_assert(
- std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
-static_assert(
- std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
-static_assert(
- std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
-static_assert(
- std::is_convertible_v<const std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
-
-// should be move constructible
-static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>);
-static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>);
struct NotConvertible {};
-// conversions should not work if there is no conversion
-static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>,
- std::ranges::out_value_result<int, NotConvertible>>);
-static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
- std::ranges::out_value_result<NotConvertible, int>>);
template <class T>
struct ConvertibleFrom {
@@ -66,6 +43,51 @@ struct ConvertibleFrom {
T content;
};
+//
+constexpr void test_constraints() {
+ // requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
+ static_assert(
+ std::is_constructible_v<std::ranges::out_value_result<int*, int>, std::ranges::out_value_result<int*, int>>);
+
+ // test failure when implicit conversion isn't allowed
+ static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeExplicit, int>,
+ std::ranges::out_value_result<int*, int>>);
+
+ // test success when implicit conversion is allowed, checking combinations of value, reference, and const
+ static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+ std::ranges::out_value_result<int*, int>>);
+ static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+ std::ranges::out_value_result<int*, int> const>);
+ static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+ std::ranges::out_value_result<int*, int>&>);
+ static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+ std::ranges::out_value_result<int*, int> const&>);
+
+ static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicitRef, int>,
+ std::ranges::out_value_result<int, int>&>);
+
+ // has to be convertible via const&
+ static_assert(
+ std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
+ static_assert(
+ std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
+ static_assert(
+ std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
+ static_assert(std::is_convertible_v<const std::ranges::out_value_result<int, int>&&,
+ std::ranges::out_value_result<long, long>>);
+
+ // should be move constructible
+ static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>);
+ static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>);
+
+ // conversions should not work if there is no conversion
+ static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>,
+ std::ranges::out_value_result<int, NotConvertible>>);
+ static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
+ std::ranges::out_value_result<NotConvertible, int>>);
+}
+
+// Test results
constexpr bool test() {
{
std::ranges::out_value_result<double, int> res{10, 1};
@@ -86,17 +108,17 @@ constexpr bool test() {
assert(res2.value == 10);
}
{
- auto [min, max] = std::ranges::out_value_result<int, int>{1, 2};
- assert(min == 1);
- assert(max == 2);
+ auto [out, val] = std::ranges::out_value_result<int, int>{1, 2};
+ assert(out == 1);
+ assert(val == 2);
}
return true;
}
int main(int, char**) {
+ test_constraints();
test();
static_assert(test());
-
return 0;
-}
+}
\ No newline at end of file
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
index 1e2a6970adb35e3..85f99942e97b2ac 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
@@ -12,41 +12,132 @@
#include <cassert>
#include <numeric>
+#include <algorithm>
#include <array>
#include "test_macros.h"
#include "test_iterators.h"
+#include "almost_satisfies_types.h"
+
+// Concepts to check different overloads of std::ranges::iota
+template <class Iter = int*, class Sent = int*, class Value = int>
+concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {
+ std::ranges::iota(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Value>(val));
+};
+
+template <class Range, class Value = int>
+concept HasIotaRange = requires(Range&& range, Value&& val) {
+ std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val));
+};
+
+constexpr void test_constraints() {
+ // Test constraints of the iterator/sentinel overload
+ // ==================================================
+ static_assert(HasIotaIter<int*, int*, int>);
+
+ // !input_or_output_iterator<O>
+ static_assert(!HasIotaIter<InputIteratorNotInputOrOutputIterator>);
+
+ // !sentinel_for<S, O>
+ static_assert(!HasIotaIter<int*, SentinelForNotSemiregular>);
+ static_assert(!HasIotaIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+ // !weakly_incrementable<T>
+ static_assert(!HasIotaIter<int*, int*, WeaklyIncrementableNotMovable>);
+
+ // !indirectly writable <O, T>
+ static_assert(!HasIotaIter<OutputIteratorNotIndirectlyWritable, int*, int>);
+
+ // Test constraints for the range overload
+ // =======================================
+ static_assert(HasIotaRange<UncheckedRange<int*>, int>);
+
+ // !weakly_incrementable<T>
+ static_assert(!HasIotaRange<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);
+
+ // !ranges::output_range<const _Tp&>
+ static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);
+}
// This is pulled directly from the std::iota test
-template <class InIter>
-TEST_CONSTEXPR_CXX20 void test0() {
+template <class InOutIter, class Sent = InOutIter>
+constexpr void test0() {
int ia[] = {1, 2, 3, 4, 5};
int ir[] = {5, 6, 7, 8, 9};
const unsigned s = sizeof(ia) / sizeof(ia[0]);
- std::ranges::iota(InIter(ia), InIter(ia + s), 5);
- for (unsigned i = 0; i < s; ++i)
- assert(ia[i] == ir[i]);
+ std::ranges::iota(InOutIter(ia), InOutIter(ia + s), 5);
+ assert(std::ranges::equal(ia, ir));
}
-TEST_CONSTEXPR_CXX20 bool test0() {
+constexpr bool test0() {
+ // TODO why don't these work?
+ // test0<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>();
+ // test0<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+ // test0<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>();
+ // test0<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>();
test0<forward_iterator<int*> >();
test0<bidirectional_iterator<int*> >();
test0<random_access_iterator<int*> >();
+ test0<contiguous_iterator<int*>>();
test0<int*>();
return true;
}
-TEST_CONSTEXPR_CXX20 void test1() {
- std::array<int, 5> ia = {1, 2, 3, 4, 5};
- std::array<int, 5> ir = {5, 6, 7, 8, 9};
- std::ranges::iota(ia, 5);
- for (unsigned i = 0; i < ir.size(); ++i)
- assert(ia[i] == ir[i]);
+template <class Iter, class Sent, std::size_t N>
+constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) {
+ { // (iterator, sentinel) overload
+ auto in_begin = Iter(input.data());
+ auto in_end = Sent(Iter(input.data() + input.size()));
+ std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =
+ std::ranges::iota(std::move(in_begin), std::move(in_end), starting_value);
+ assert(result.out == in_end);
+ if constexpr (expected.size() > 0) {
+ assert(result.value == expected.back() + 1);
+ } else {
+ assert(result.value == starting_value);
+ }
+ assert(std::ranges::equal(input, expected));
+ }
+
+ { // (range) overload
+ auto in_begin = Iter(input.data());
+ auto in_end = Sent(Iter(input.data() + input.size()));
+ auto range = std::ranges::subrange(std::move(in_begin), std::move(in_end));
+
+ std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =
+ std::ranges::iota(range, starting_value);
+ assert(result.out == in_end);
+ if constexpr (expected.size() > 0) {
+ assert(result.value == expected.back() + 1);
+ } else {
+ assert(result.value == starting_value);
+ }
+ assert(std::ranges::equal(input, expected));
+ }
+}
+
+constexpr bool test_results() {
+ using Iter = forward_iterator<int*>;
+ using Sent = sentinel_wrapper<Iter>;
+
+ // Empty
+ test_result<Iter, Sent, 0>({}, 0, {});
+ // 1-element sequence
+ test_result<Iter, Sent, 1>({1}, 0, {0});
+ // Longer sequence
+ test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
+
+ return true;
}
int main(int, char**) {
+ test_constraints();
+
test0();
- test1();
+ static_assert(test0());
+
+ test_results();
+ static_assert(test_results());
return 0;
}
\ No newline at end of file
>From a1d015c2affa40bb5f5313cdaed46c5a8cd4b243 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 7 Oct 2023 12:35:20 -0400
Subject: [PATCH 5/6] [libc++] Implement ranges::iota: Cleaning up tests for
ranges::iota and adding testing for more iterators
---
.../numeric.iota/ranges.iota.pass.cpp | 53 ++++++-------------
1 file changed, 17 insertions(+), 36 deletions(-)
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
index 85f99942e97b2ac..611293debbf731b 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
@@ -59,31 +59,6 @@ constexpr void test_constraints() {
static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);
}
-// This is pulled directly from the std::iota test
-template <class InOutIter, class Sent = InOutIter>
-constexpr void test0() {
- int ia[] = {1, 2, 3, 4, 5};
- int ir[] = {5, 6, 7, 8, 9};
- const unsigned s = sizeof(ia) / sizeof(ia[0]);
- std::ranges::iota(InOutIter(ia), InOutIter(ia + s), 5);
- assert(std::ranges::equal(ia, ir));
-}
-
-constexpr bool test0() {
- // TODO why don't these work?
- // test0<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>();
- // test0<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
- // test0<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>();
- // test0<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>();
- test0<forward_iterator<int*> >();
- test0<bidirectional_iterator<int*> >();
- test0<random_access_iterator<int*> >();
- test0<contiguous_iterator<int*>>();
- test0<int*>();
-
- return true;
-}
-
template <class Iter, class Sent, std::size_t N>
constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) {
{ // (iterator, sentinel) overload
@@ -100,7 +75,10 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
assert(std::ranges::equal(input, expected));
}
- { // (range) overload
+ // The range overload adds the additional constraint that it must be an outputrange
+ // so skip this for the input iterators we test
+ if constexpr (!std::is_same_v<Iter, cpp17_input_iterator<int*>> &&
+ !std::is_same_v<Iter, cpp20_input_iterator<int*>>) { // (range) overload
auto in_begin = Iter(input.data());
auto in_end = Sent(Iter(input.data() + input.size()));
auto range = std::ranges::subrange(std::move(in_begin), std::move(in_end));
@@ -117,27 +95,30 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
}
}
-constexpr bool test_results() {
- using Iter = forward_iterator<int*>;
- using Sent = sentinel_wrapper<Iter>;
-
+template <class Iter, class Sent = sentinel_wrapper<Iter>>
+constexpr void test_results() {
// Empty
test_result<Iter, Sent, 0>({}, 0, {});
// 1-element sequence
test_result<Iter, Sent, 1>({1}, 0, {0});
// Longer sequence
test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
+}
- return true;
+void test_results() {
+ test_results<cpp17_input_iterator<int*>>();
+ test_results<cpp20_input_iterator<int*>>();
+ test_results<cpp17_output_iterator<int*>>();
+ test_results<cpp20_output_iterator<int*>>();
+ test_results<forward_iterator<int*>>();
+ test_results<bidirectional_iterator<int*>>();
+ test_results<random_access_iterator<int*>>();
+ test_results<contiguous_iterator<int*>>();
+ test_results<int*>();
}
int main(int, char**) {
test_constraints();
-
- test0();
- static_assert(test0());
-
test_results();
- static_assert(test_results());
return 0;
}
\ No newline at end of file
>From ab4c67d3537379aab5117ac8dad232c5072991e9 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 7 Oct 2023 12:40:02 -0400
Subject: [PATCH 6/6] [libc++] Implement ranges::iota: Using clang-format-17 to
make CI checks happy
---
.../numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
index 611293debbf731b..3a7bc4319bebd6e 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
@@ -26,9 +26,8 @@ concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {
};
template <class Range, class Value = int>
-concept HasIotaRange = requires(Range&& range, Value&& val) {
- std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val));
-};
+concept HasIotaRange =
+ requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); };
constexpr void test_constraints() {
// Test constraints of the iterator/sentinel overload
More information about the libcxx-commits
mailing list