Skip to content

Commit c3648f3

Browse files
committed
[libc++][ranges] Implement ranges::to.
Differential Revision: https://reviews.llvm.org/D142335
1 parent 7baf5d3 commit c3648f3

32 files changed

+1645
-283
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ Status
358358
--------------------------------------------------- -----------------
359359
``__cpp_lib_ranges_starts_ends_with`` *unimplemented*
360360
--------------------------------------------------- -----------------
361-
``__cpp_lib_ranges_to_container`` *unimplemented*
361+
``__cpp_lib_ranges_to_container`` ``202202L``
362362
--------------------------------------------------- -----------------
363363
``__cpp_lib_ranges_zip`` *unimplemented*
364364
--------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/17.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Work has started on the C++17 Parallel STL. This feature is experimental, see
5555

5656
Implemented Papers
5757
------------------
58+
- P1206R7 - ``ranges::to``: A function to convert any range to a container
5859
- P2520R0 - ``move_iterator<T*>`` should be a random access iterator
5960
- P1328R1 - ``constexpr type_info::operator==()``
6061
- P1413R3 - Formatting ``thread::id`` (the ``stacktrace`` is not done yet)

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@
258258
"`3820 <https://wg21.link/LWG3820>`__","``cartesian_product_view::iterator::prev`` is not quite right","February 2023","","","|ranges|"
259259
"`3825 <https://wg21.link/LWG3825>`__","Missing compile-time argument ``id`` check in ``basic_format_parse_context::next_arg_id``","February 2023","|Complete|","17.0","|format|"
260260
"`3204 <https://wg21.link/LWG3204>`__","``sub_match::swap`` only swaps the base class","February 2023","|Complete|","17.0",""
261-
"`3733 <https://wg21.link/LWG3733>`__","``ranges::to`` misuses ``cpp17-input-iterator``","February 2023","","","|ranges|"
261+
"`3733 <https://wg21.link/LWG3733>`__","``ranges::to`` misuses ``cpp17-input-iterator``","February 2023","|Complete|","17.0","|ranges|"
262262
"`3742 <https://wg21.link/LWG3742>`__","``deque::prepend_range`` needs to permute","February 2023","","","|ranges|"
263263
"`3790 <https://wg21.link/LWG3790>`__","`P1467 <https://wg21.link/P1467>`__ accidentally changed ``nexttoward``'s signature","February 2023","","",""
264264
"`3819 <https://wg21.link/LWG3819>`__","``reference_meows_from_temporary`` should not use ``is_meowible``","February 2023","","",""
@@ -292,7 +292,7 @@
292292
"`3833 <https://wg21.link/LWG3833>`__","Remove specialization ``template<size_t N> struct formatter<const charT[N], charT>``","February 2023","|Complete|","17.0","|format|"
293293
"`3836 <https://wg21.link/LWG3836>`__","``std::expected<bool, E1>`` conversion constructor ``expected(const expected<U, G>&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","February 2023","","",""
294294
"`3843 <https://wg21.link/LWG3843>`__","``std::expected<T,E>::value() &`` assumes ``E`` is copy constructible","February 2023","|Complete|","17.0",""
295-
"`3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","February 2023","","","|ranges|"
295+
"`3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","February 2023","|Complete|","17.0","|ranges|"
296296
"`3862 <https://wg21.link/LWG3862>`__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","February 2023","","",""
297297
"`3865 <https://wg21.link/LWG3865>`__","Sorting a range of ``pairs``","February 2023","|Complete|","17.0","|ranges|"
298298
"`3869 <https://wg21.link/LWG3869>`__","Deprecate ``std::errc`` constants related to UNIX STREAMS","February 2023","","",""

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"`P0323R12 <https://wg21.link/P0323R12>`__","LWG","``std::expected``","February 2022","|Complete|","16.0"
4242
"`P0533R9 <https://wg21.link/P0533R9>`__","LWG","``constexpr`` for ``<cmath>`` and ``<cstdlib>``","February 2022","|In progress| [#note-P0533R9]_",""
4343
"`P0627R6 <https://wg21.link/P0627R6>`__","LWG","Function to mark unreachable code","February 2022","|Complete|","15.0"
44-
"`P1206R7 <https://wg21.link/P1206R7>`__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","|In Progress|","","|ranges|"
44+
"`P1206R7 <https://wg21.link/P1206R7>`__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","|Complete|","17.0","|ranges|"
4545
"`P1413R3 <https://wg21.link/P1413R3>`__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_",""
4646
"`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
4747
"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
Standard,Name,Assignee,CL,Status
2-
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Unassigned,No patch yet,Not started
2+
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete
33
C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started
44
C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ set(files
646646
__ranges/subrange.h
647647
__ranges/take_view.h
648648
__ranges/take_while_view.h
649+
__ranges/to.h
649650
__ranges/transform_view.h
650651
__ranges/view_interface.h
651652
__ranges/views.h

libcxx/include/__ranges/to.h

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___RANGES_TO_H
11+
#define _LIBCPP___RANGES_TO_H
12+
13+
#include <__algorithm/ranges_copy.h>
14+
#include <__concepts/constructible.h>
15+
#include <__concepts/convertible_to.h>
16+
#include <__concepts/derived_from.h>
17+
#include <__concepts/same_as.h>
18+
#include <__config>
19+
#include <__functional/bind_back.h>
20+
#include <__iterator/back_insert_iterator.h>
21+
#include <__iterator/insert_iterator.h>
22+
#include <__iterator/iterator_traits.h>
23+
#include <__ranges/access.h>
24+
#include <__ranges/concepts.h>
25+
#include <__ranges/from_range.h>
26+
#include <__ranges/range_adaptor.h>
27+
#include <__ranges/size.h>
28+
#include <__ranges/transform_view.h>
29+
#include <__type_traits/add_pointer.h>
30+
#include <__type_traits/is_const.h>
31+
#include <__type_traits/is_volatile.h>
32+
#include <__type_traits/type_identity.h>
33+
#include <__utility/declval.h>
34+
#include <__utility/forward.h>
35+
#include <cstddef>
36+
37+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
38+
# pragma GCC system_header
39+
#endif
40+
41+
_LIBCPP_BEGIN_NAMESPACE_STD
42+
43+
#if _LIBCPP_STD_VER >= 23
44+
45+
namespace ranges {
46+
47+
// TODO(clang-15): in the Standard, it's a `constexpr bool` variable, not a concept, but constexpr variables don't
48+
// short-circuit properly on Clang 15 (fixed in later versions), so use a concept as a workaround.
49+
template <class _Container>
50+
concept __reservable_container = sized_range<_Container> && requires(_Container& __c, range_size_t<_Container> __n) {
51+
__c.reserve(__n);
52+
{ __c.capacity() } -> same_as<decltype(__n)>;
53+
{ __c.max_size() } -> same_as<decltype(__n)>;
54+
};
55+
56+
template <class _Container, class _Ref>
57+
constexpr bool __container_insertable = requires(_Container& __c, _Ref&& __ref) {
58+
requires(
59+
requires { __c.push_back(std::forward<_Ref>(__ref)); } ||
60+
requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); });
61+
};
62+
63+
template <class _Ref, class _Container>
64+
_LIBCPP_HIDE_FROM_ABI constexpr auto __container_inserter(_Container& __c) {
65+
if constexpr (requires { __c.push_back(std::declval<_Ref>()); }) {
66+
return std::back_inserter(__c);
67+
} else {
68+
return std::inserter(__c, __c.end());
69+
}
70+
}
71+
72+
// Note: making this a concept allows short-circuiting the second condition.
73+
template <class _Container, class _Range>
74+
concept __try_non_recursive_conversion =
75+
!input_range<_Container> || convertible_to<range_reference_t<_Range>, range_value_t<_Container>>;
76+
77+
template <class _Container, class _Range, class... _Args>
78+
concept __constructible_from_iter_pair =
79+
common_range<_Range> && requires { typename iterator_traits<iterator_t<_Range>>::iterator_category; } &&
80+
derived_from<typename iterator_traits<iterator_t<_Range>>::iterator_category, input_iterator_tag> &&
81+
constructible_from<_Container, iterator_t<_Range>, sentinel_t<_Range>, _Args...>;
82+
83+
template <class>
84+
concept __always_false = false;
85+
86+
// `ranges::to` base template -- the `_Container` type is a simple type template parameter.
87+
template <class _Container, input_range _Range, class... _Args>
88+
requires(!view<_Container>)
89+
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _Container to(_Range&& __range, _Args&&... __args) {
90+
// Mandates: C is a cv-unqualified class type.
91+
static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
92+
static_assert(
93+
!is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
94+
95+
// First see if the non-recursive case applies -- the conversion target is either:
96+
// - a range with a convertible value type;
97+
// - a non-range type which might support being created from the input argument(s) (e.g. an `optional`).
98+
if constexpr (__try_non_recursive_conversion<_Container, _Range>) {
99+
// Case 1 -- construct directly from the given range.
100+
if constexpr (constructible_from<_Container, _Range, _Args...>) {
101+
return _Container(std::forward<_Range>(__range), std::forward<_Args>(__args)...);
102+
}
103+
104+
// Case 2 -- construct using the `from_range_t` tagged constructor.
105+
else if constexpr (constructible_from<_Container, from_range_t, _Range, _Args...>) {
106+
return _Container(from_range, std::forward<_Range>(__range), std::forward<_Args>(__args)...);
107+
}
108+
109+
// Case 3 -- construct from a begin-end iterator pair.
110+
else if constexpr (__constructible_from_iter_pair<_Container, _Range, _Args...>) {
111+
return _Container(ranges::begin(__range), ranges::end(__range), std::forward<_Args>(__args)...);
112+
}
113+
114+
// Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
115+
else if constexpr (constructible_from<_Container, _Args...> &&
116+
__container_insertable<_Container, range_reference_t<_Range>>) {
117+
_Container __result(std::forward<_Args>(__args)...);
118+
if constexpr (sized_range<_Range> && __reservable_container<_Container>) {
119+
__result.reserve(static_cast<range_size_t<_Container>>(ranges::size(__range)));
120+
}
121+
122+
ranges::copy(__range, ranges::__container_inserter<range_reference_t<_Range>>(__result));
123+
124+
return __result;
125+
126+
} else {
127+
static_assert(__always_false<_Container>, "ranges::to: unable to convert to the given container type.");
128+
}
129+
130+
// Try the recursive case.
131+
} else if constexpr (input_range<range_reference_t<_Range>>) {
132+
return ranges::to<_Container>(
133+
__range | views::transform([](auto&& __elem) {
134+
return ranges::to<range_value_t<_Container>>(std::forward<decltype(__elem)>(__elem));
135+
}),
136+
std::forward<_Args>(__args)...);
137+
138+
} else {
139+
static_assert(__always_false<_Container>, "ranges::to: unable to convert to the given container type.");
140+
}
141+
}
142+
143+
template <class _Range>
144+
struct __minimal_input_iterator {
145+
using iterator_category = input_iterator_tag;
146+
using value_type = range_value_t<_Range>;
147+
using difference_type = ptrdiff_t;
148+
using pointer = add_pointer_t<range_reference_t<_Range>>;
149+
using reference = range_reference_t<_Range>;
150+
151+
reference operator*() const;
152+
pointer operator->() const;
153+
__minimal_input_iterator& operator++();
154+
__minimal_input_iterator operator++(int);
155+
bool operator==(const __minimal_input_iterator&) const;
156+
};
157+
158+
// Deduces the full type of the container from the given template template parameter.
159+
template <template <class...> class _Container, input_range _Range, class... _Args>
160+
struct _Deducer {
161+
_LIBCPP_HIDE_FROM_ABI static constexpr auto __deduce_func() {
162+
using _InputIter = __minimal_input_iterator<_Range>;
163+
164+
// Case 1 -- can construct directly from the given range.
165+
if constexpr (requires { _Container(std::declval<_Range>(), std::declval<_Args>()...); }) {
166+
using _Result = decltype( //
167+
_Container(std::declval<_Range>(), std::declval<_Args>()...));
168+
return type_identity<_Result>{};
169+
170+
// Case 2 -- can construct from the given range using the `from_range_t` tagged constructor.
171+
} else if constexpr ( //
172+
requires { _Container(from_range, std::declval<_Range>(), std::declval<_Args>()...); }) {
173+
using _Result = //
174+
decltype(_Container(from_range, std::declval<_Range>(), std::declval<_Args>()...));
175+
return type_identity<_Result>{};
176+
177+
// Case 3 -- can construct from a begin-end iterator pair.
178+
} else if constexpr ( //
179+
requires { _Container(std::declval<_InputIter>(), std::declval<_InputIter>(), std::declval<_Args>()...); }) {
180+
using _Result =
181+
decltype(_Container(std::declval<_InputIter>(), std::declval<_InputIter>(), std::declval<_Args>()...));
182+
return type_identity<_Result>{};
183+
184+
} else {
185+
static_assert(__always_false<_Range>,
186+
"ranges::to: unable to deduce the container type from the template template argument.");
187+
}
188+
}
189+
190+
using type = typename decltype(__deduce_func())::type;
191+
};
192+
193+
// `ranges::to` specialization -- `_Container` is a template template parameter requiring deduction to figure out the
194+
// container element type.
195+
template <template <class...> class _Container, input_range _Range, class... _Args>
196+
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Range&& __range, _Args&&... __args) {
197+
using _DeduceExpr = typename _Deducer<_Container, _Range, _Args...>::type;
198+
return ranges::to<_DeduceExpr>(std::forward<_Range>(__range), std::forward<_Args>(__args)...);
199+
}
200+
201+
// Range adaptor closure object 1 -- wrapping the `ranges::to` version where `_Container` is a simple type template
202+
// parameter.
203+
template <class _Container, class... _Args>
204+
requires(!view<_Container>)
205+
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Args&&... __args) {
206+
// Mandates: C is a cv-unqualified class type.
207+
static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
208+
static_assert(
209+
!is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
210+
211+
auto __to_func = []<input_range _Range, class... _Tail>(_Range && __range, _Tail && ... __tail)
212+
requires requires { //
213+
/**/ ranges::to<_Container>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
214+
}
215+
{
216+
return ranges::to<_Container>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
217+
};
218+
219+
return __range_adaptor_closure_t(std::__bind_back(__to_func, std::forward<_Args>(__args)...));
220+
}
221+
222+
// Range adaptor closure object 2 -- wrapping the `ranges::to` version where `_Container` is a template template
223+
// parameter.
224+
template <template <class...> class _Container, class... _Args>
225+
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Args&&... __args) {
226+
// clang-format off
227+
auto __to_func = []<input_range _Range, class... _Tail,
228+
class _DeducedExpr = typename _Deducer<_Container, _Range, _Tail...>::type>
229+
(_Range&& __range, _Tail&& ... __tail)
230+
requires requires { //
231+
/**/ ranges::to<_DeducedExpr>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
232+
}
233+
{
234+
return ranges::to<_DeducedExpr>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
235+
};
236+
// clang-format on
237+
238+
return __range_adaptor_closure_t(std::__bind_back(__to_func, std::forward<_Args>(__args)...));
239+
}
240+
241+
} // namespace ranges
242+
243+
#endif // _LIBCPP_STD_VER >= 23
244+
245+
_LIBCPP_END_NAMESPACE_STD
246+
247+
#endif // _LIBCPP___RANGES_TO_H

libcxx/include/module.modulemap.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,7 @@ module std_private_ranges_subrange_fwd [system] {
17181718
}
17191719
module std_private_ranges_take_view [system] { header "__ranges/take_view.h" }
17201720
module std_private_ranges_take_while_view [system] { header "__ranges/take_while_view.h" }
1721+
module std_private_ranges_to [system] { header "__ranges/to.h" }
17211722
module std_private_ranges_transform_view [system] {
17221723
header "__ranges/transform_view.h"
17231724
export std_private_functional_bind_back

libcxx/include/ranges

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ namespace std::ranges {
138138
inline constexpr auto values = elements<1>;
139139
}
140140
141+
// [range.utility.conv], range conversions
142+
template<class C, input_range R, class... Args> requires (!view<C>)
143+
constexpr C to(R&& r, Args&&... args); // Since C++23
144+
template<template<class...> class C, input_range R, class... Args>
145+
constexpr auto to(R&& r, Args&&... args); // Since C++23
146+
template<class C, class... Args> requires (!view<C>)
147+
constexpr auto to(Args&&... args); // Since C++23
148+
template<template<class...> class C, class... Args>
149+
constexpr auto to(Args&&... args); // Since C++23
150+
141151
// [range.empty], empty view
142152
template<class T>
143153
requires is_object_v<T>
@@ -391,6 +401,7 @@ namespace std {
391401
#include <__ranges/subrange.h>
392402
#include <__ranges/take_view.h>
393403
#include <__ranges/take_while_view.h>
404+
#include <__ranges/to.h>
394405
#include <__ranges/transform_view.h>
395406
#include <__ranges/view_interface.h>
396407
#include <__ranges/views.h>

libcxx/include/version

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ __cpp_lib_ranges_repeat 202207L <ranges>
162162
__cpp_lib_ranges_slide 202202L <ranges>
163163
__cpp_lib_ranges_starts_ends_with 202106L <algorithm>
164164
__cpp_lib_ranges_to_container 202202L <deque> <forward_list> <list>
165-
<map> <priority_queue> <queue>
165+
<map> <queue> <ranges>
166166
<set> <stack> <string>
167167
<unordered_map> <unordered_set> <vector>
168168
__cpp_lib_ranges_zip 202110L <ranges> <tuple> <utility>
@@ -440,7 +440,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
440440
# define __cpp_lib_ranges_repeat 202207L
441441
// # define __cpp_lib_ranges_slide 202202L
442442
// # define __cpp_lib_ranges_starts_ends_with 202106L
443-
// # define __cpp_lib_ranges_to_container 202202L
443+
# define __cpp_lib_ranges_to_container 202202L
444444
// # define __cpp_lib_ranges_zip 202110L
445445
// # define __cpp_lib_reference_from_temporary 202202L
446446
// # define __cpp_lib_spanstream 202106L

0 commit comments

Comments
 (0)