From 7a6a29c455e7755b501c0006e39beb4e56ec2729 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 19 Sep 2023 13:23:13 +0100 Subject: [PATCH] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055] This adds the std::ranges::to functions for C++23. The rest of P1206R7 is not yet implemented, i.e. the new constructors taking the std::from_range tag, and the new insert_range, assign_range, etc. member functions. std::ranges::to works with the standard containers even without the new constructors, so this is useful immediately. The __cpp_lib_ranges_to_container feature test macro can be defined now, because that only indicates support for the changes in , which are implemented by this patch. The __cpp_lib_containers_ranges macro will be defined once all containers support the new member functions. libstdc++-v3/ChangeLog: PR libstdc++/111055 * include/bits/ranges_base.h (from_range_t): Define new tag type. (from_range): Define new tag object. * include/bits/version.def (ranges_to_container): Define. * include/bits/version.h: Regenerate. * include/std/ranges (ranges::to): Define. * testsuite/std/ranges/conv/1.cc: New test. * testsuite/std/ranges/conv/2_neg.cc: New test. * testsuite/std/ranges/conv/version.cc: New test. --- libstdc++-v3/include/bits/ranges_base.h | 8 +- libstdc++-v3/include/bits/version.def | 34 +- libstdc++-v3/include/bits/version.h | 111 ++++--- libstdc++-v3/include/std/ranges | 361 ++++++++++++++++++++- libstdc++-v3/testsuite/std/ranges/conv/1.cc | 369 ++++++++++++++++++++++ libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc | 24 ++ libstdc++-v3/testsuite/std/ranges/conv/version.cc | 19 ++ 7 files changed, 866 insertions(+), 60 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/1.cc create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/version.cc diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index 7fa43d1965a..1ca2c5ce2bb 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef __cpp_lib_concepts namespace std _GLIBCXX_VISIBILITY(default) @@ -1056,8 +1057,13 @@ namespace ranges using borrowed_iterator_t = __conditional_t, iterator_t<_Range>, dangling>; - } // namespace ranges + +#if __glibcxx_ranges_to_container // C++ >= 23 + struct from_range_t { explicit from_range_t() = default; }; + inline constexpr from_range_t from_range{}; +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // library concepts diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 605708dfee7..140777832ed 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1439,19 +1439,21 @@ ftms = { }; }; -ftms = { - name = to_underlying; - values = { - v = 202102; - cxxmin = 23; - }; -}; +//ftms = { +// name = container_ranges; +// values = { +// v = 202202; +// cxxmin = 23; +// hosted = yes; +// }; +//}; ftms = { - name = unreachable; + name = ranges_to_container; values = { v = 202202; cxxmin = 23; + hosted = yes; }; }; @@ -1684,6 +1686,22 @@ ftms = { }; ftms = { + name = to_underlying; + values = { + v = 202102; + cxxmin = 23; + }; +}; + +ftms = { + name = unreachable; + values = { + v = 202202; + cxxmin = 23; + }; +}; + +ftms = { name = fstream_native_handle; values = { v = 202306; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index cacd9375cab..1fb1d148459 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1740,29 +1740,18 @@ #endif /* !defined(__cpp_lib_reference_from_temporary) && defined(__glibcxx_want_reference_from_temporary) */ #undef __glibcxx_want_reference_from_temporary -// from version.def line 1443 -#if !defined(__cpp_lib_to_underlying) -# if (__cplusplus >= 202100L) -# define __glibcxx_to_underlying 202102L -# if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying) -# define __cpp_lib_to_underlying 202102L -# endif -# endif -#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */ -#undef __glibcxx_want_to_underlying - -// from version.def line 1451 -#if !defined(__cpp_lib_unreachable) -# if (__cplusplus >= 202100L) -# define __glibcxx_unreachable 202202L -# if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable) -# define __cpp_lib_unreachable 202202L +// from version.def line 1452 +#if !defined(__cpp_lib_ranges_to_container) +# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED +# define __glibcxx_ranges_to_container 202202L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_container) +# define __cpp_lib_ranges_to_container 202202L # endif # endif -#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */ -#undef __glibcxx_want_unreachable +#endif /* !defined(__cpp_lib_ranges_to_container) && defined(__glibcxx_want_ranges_to_container) */ +#undef __glibcxx_want_ranges_to_container -// from version.def line 1459 +// from version.def line 1461 #if !defined(__cpp_lib_ranges_zip) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_zip 202110L @@ -1773,7 +1762,7 @@ #endif /* !defined(__cpp_lib_ranges_zip) && defined(__glibcxx_want_ranges_zip) */ #undef __glibcxx_want_ranges_zip -// from version.def line 1467 +// from version.def line 1469 #if !defined(__cpp_lib_ranges_chunk) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_chunk 202202L @@ -1784,7 +1773,7 @@ #endif /* !defined(__cpp_lib_ranges_chunk) && defined(__glibcxx_want_ranges_chunk) */ #undef __glibcxx_want_ranges_chunk -// from version.def line 1475 +// from version.def line 1477 #if !defined(__cpp_lib_ranges_slide) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_slide 202202L @@ -1795,7 +1784,7 @@ #endif /* !defined(__cpp_lib_ranges_slide) && defined(__glibcxx_want_ranges_slide) */ #undef __glibcxx_want_ranges_slide -// from version.def line 1483 +// from version.def line 1485 #if !defined(__cpp_lib_ranges_chunk_by) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_chunk_by 202202L @@ -1806,7 +1795,7 @@ #endif /* !defined(__cpp_lib_ranges_chunk_by) && defined(__glibcxx_want_ranges_chunk_by) */ #undef __glibcxx_want_ranges_chunk_by -// from version.def line 1491 +// from version.def line 1493 #if !defined(__cpp_lib_ranges_join_with) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_join_with 202202L @@ -1817,7 +1806,7 @@ #endif /* !defined(__cpp_lib_ranges_join_with) && defined(__glibcxx_want_ranges_join_with) */ #undef __glibcxx_want_ranges_join_with -// from version.def line 1499 +// from version.def line 1501 #if !defined(__cpp_lib_ranges_repeat) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_repeat 202207L @@ -1828,7 +1817,7 @@ #endif /* !defined(__cpp_lib_ranges_repeat) && defined(__glibcxx_want_ranges_repeat) */ #undef __glibcxx_want_ranges_repeat -// from version.def line 1507 +// from version.def line 1509 #if !defined(__cpp_lib_ranges_stride) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_stride 202207L @@ -1839,7 +1828,7 @@ #endif /* !defined(__cpp_lib_ranges_stride) && defined(__glibcxx_want_ranges_stride) */ #undef __glibcxx_want_ranges_stride -// from version.def line 1515 +// from version.def line 1517 #if !defined(__cpp_lib_ranges_cartesian_product) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_cartesian_product 202207L @@ -1850,7 +1839,7 @@ #endif /* !defined(__cpp_lib_ranges_cartesian_product) && defined(__glibcxx_want_ranges_cartesian_product) */ #undef __glibcxx_want_ranges_cartesian_product -// from version.def line 1523 +// from version.def line 1525 #if !defined(__cpp_lib_ranges_as_rvalue) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_as_rvalue 202207L @@ -1861,7 +1850,7 @@ #endif /* !defined(__cpp_lib_ranges_as_rvalue) && defined(__glibcxx_want_ranges_as_rvalue) */ #undef __glibcxx_want_ranges_as_rvalue -// from version.def line 1531 +// from version.def line 1533 #if !defined(__cpp_lib_ranges_as_const) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_as_const 202207L @@ -1872,7 +1861,7 @@ #endif /* !defined(__cpp_lib_ranges_as_const) && defined(__glibcxx_want_ranges_as_const) */ #undef __glibcxx_want_ranges_as_const -// from version.def line 1539 +// from version.def line 1541 #if !defined(__cpp_lib_ranges_enumerate) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_enumerate 202302L @@ -1883,7 +1872,7 @@ #endif /* !defined(__cpp_lib_ranges_enumerate) && defined(__glibcxx_want_ranges_enumerate) */ #undef __glibcxx_want_ranges_enumerate -// from version.def line 1547 +// from version.def line 1549 #if !defined(__cpp_lib_ranges_fold) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_fold 202207L @@ -1894,7 +1883,7 @@ #endif /* !defined(__cpp_lib_ranges_fold) && defined(__glibcxx_want_ranges_fold) */ #undef __glibcxx_want_ranges_fold -// from version.def line 1555 +// from version.def line 1557 #if !defined(__cpp_lib_ranges_contains) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_contains 202207L @@ -1905,7 +1894,7 @@ #endif /* !defined(__cpp_lib_ranges_contains) && defined(__glibcxx_want_ranges_contains) */ #undef __glibcxx_want_ranges_contains -// from version.def line 1563 +// from version.def line 1565 #if !defined(__cpp_lib_ranges_iota) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_iota 202202L @@ -1916,7 +1905,7 @@ #endif /* !defined(__cpp_lib_ranges_iota) && defined(__glibcxx_want_ranges_iota) */ #undef __glibcxx_want_ranges_iota -// from version.def line 1571 +// from version.def line 1573 #if !defined(__cpp_lib_ranges_find_last) # if (__cplusplus >= 202100L) # define __glibcxx_ranges_find_last 202207L @@ -1927,7 +1916,7 @@ #endif /* !defined(__cpp_lib_ranges_find_last) && defined(__glibcxx_want_ranges_find_last) */ #undef __glibcxx_want_ranges_find_last -// from version.def line 1579 +// from version.def line 1581 #if !defined(__cpp_lib_constexpr_bitset) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (__cpp_constexpr_dynamic_alloc) # define __glibcxx_constexpr_bitset 202202L @@ -1938,7 +1927,7 @@ #endif /* !defined(__cpp_lib_constexpr_bitset) && defined(__glibcxx_want_constexpr_bitset) */ #undef __glibcxx_want_constexpr_bitset -// from version.def line 1589 +// from version.def line 1591 #if !defined(__cpp_lib_stdatomic_h) # if (__cplusplus >= 202100L) # define __glibcxx_stdatomic_h 202011L @@ -1949,7 +1938,7 @@ #endif /* !defined(__cpp_lib_stdatomic_h) && defined(__glibcxx_want_stdatomic_h) */ #undef __glibcxx_want_stdatomic_h -// from version.def line 1597 +// from version.def line 1599 #if !defined(__cpp_lib_adaptor_iterator_pair_constructor) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED # define __glibcxx_adaptor_iterator_pair_constructor 202106L @@ -1960,7 +1949,7 @@ #endif /* !defined(__cpp_lib_adaptor_iterator_pair_constructor) && defined(__glibcxx_want_adaptor_iterator_pair_constructor) */ #undef __glibcxx_want_adaptor_iterator_pair_constructor -// from version.def line 1606 +// from version.def line 1608 #if !defined(__cpp_lib_formatters) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED # define __glibcxx_formatters 202302L @@ -1971,7 +1960,7 @@ #endif /* !defined(__cpp_lib_formatters) && defined(__glibcxx_want_formatters) */ #undef __glibcxx_want_formatters -// from version.def line 1615 +// from version.def line 1617 #if !defined(__cpp_lib_forward_like) # if (__cplusplus >= 202100L) # define __glibcxx_forward_like 202207L @@ -1982,7 +1971,7 @@ #endif /* !defined(__cpp_lib_forward_like) && defined(__glibcxx_want_forward_like) */ #undef __glibcxx_want_forward_like -// from version.def line 1623 +// from version.def line 1625 #if !defined(__cpp_lib_ios_noreplace) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED # define __glibcxx_ios_noreplace 202207L @@ -1993,7 +1982,7 @@ #endif /* !defined(__cpp_lib_ios_noreplace) && defined(__glibcxx_want_ios_noreplace) */ #undef __glibcxx_want_ios_noreplace -// from version.def line 1632 +// from version.def line 1634 #if !defined(__cpp_lib_move_only_function) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED # define __glibcxx_move_only_function 202110L @@ -2004,7 +1993,7 @@ #endif /* !defined(__cpp_lib_move_only_function) && defined(__glibcxx_want_move_only_function) */ #undef __glibcxx_want_move_only_function -// from version.def line 1641 +// from version.def line 1643 #if !defined(__cpp_lib_out_ptr) # if (__cplusplus >= 202100L) # define __glibcxx_out_ptr 202311L @@ -2015,7 +2004,7 @@ #endif /* !defined(__cpp_lib_out_ptr) && defined(__glibcxx_want_out_ptr) */ #undef __glibcxx_want_out_ptr -// from version.def line 1649 +// from version.def line 1651 #if !defined(__cpp_lib_spanstream) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (__glibcxx_span) # define __glibcxx_spanstream 202106L @@ -2026,7 +2015,7 @@ #endif /* !defined(__cpp_lib_spanstream) && defined(__glibcxx_want_spanstream) */ #undef __glibcxx_want_spanstream -// from version.def line 1659 +// from version.def line 1661 #if !defined(__cpp_lib_stacktrace) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (_GLIBCXX_HAVE_STACKTRACE) # define __glibcxx_stacktrace 202011L @@ -2037,7 +2026,7 @@ #endif /* !defined(__cpp_lib_stacktrace) && defined(__glibcxx_want_stacktrace) */ #undef __glibcxx_want_stacktrace -// from version.def line 1669 +// from version.def line 1671 #if !defined(__cpp_lib_string_contains) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED # define __glibcxx_string_contains 202011L @@ -2048,7 +2037,7 @@ #endif /* !defined(__cpp_lib_string_contains) && defined(__glibcxx_want_string_contains) */ #undef __glibcxx_want_string_contains -// from version.def line 1678 +// from version.def line 1680 #if !defined(__cpp_lib_string_resize_and_overwrite) # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED # define __glibcxx_string_resize_and_overwrite 202110L @@ -2059,7 +2048,29 @@ #endif /* !defined(__cpp_lib_string_resize_and_overwrite) && defined(__glibcxx_want_string_resize_and_overwrite) */ #undef __glibcxx_want_string_resize_and_overwrite -// from version.def line 1687 +// from version.def line 1689 +#if !defined(__cpp_lib_to_underlying) +# if (__cplusplus >= 202100L) +# define __glibcxx_to_underlying 202102L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying) +# define __cpp_lib_to_underlying 202102L +# endif +# endif +#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */ +#undef __glibcxx_want_to_underlying + +// from version.def line 1697 +#if !defined(__cpp_lib_unreachable) +# if (__cplusplus >= 202100L) +# define __glibcxx_unreachable 202202L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable) +# define __cpp_lib_unreachable 202202L +# endif +# endif +#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */ +#undef __glibcxx_want_unreachable + +// from version.def line 1705 #if !defined(__cpp_lib_fstream_native_handle) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED # define __glibcxx_fstream_native_handle 202306L @@ -2070,7 +2081,7 @@ #endif /* !defined(__cpp_lib_fstream_native_handle) && defined(__glibcxx_want_fstream_native_handle) */ #undef __glibcxx_want_fstream_native_handle -// from version.def line 1696 +// from version.def line 1714 #if !defined(__cpp_lib_ratio) # if (__cplusplus > 202302L) # define __glibcxx_ratio 202306L @@ -2081,7 +2092,7 @@ #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */ #undef __glibcxx_want_ratio -// from version.def line 1704 +// from version.def line 1722 #if !defined(__cpp_lib_saturation_arithmetic) # if (__cplusplus > 202302L) # define __glibcxx_saturation_arithmetic 202311L @@ -2092,7 +2103,7 @@ #endif /* !defined(__cpp_lib_saturation_arithmetic) && defined(__glibcxx_want_saturation_arithmetic) */ #undef __glibcxx_want_saturation_arithmetic -// from version.def line 1712 +// from version.def line 1730 #if !defined(__cpp_lib_to_string) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) # define __glibcxx_to_string 202306L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 26d6c013ad0..63bea862c05 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -64,6 +64,7 @@ #define __glibcxx_want_ranges_repeat #define __glibcxx_want_ranges_slide #define __glibcxx_want_ranges_stride +#define __glibcxx_want_ranges_to_container #define __glibcxx_want_ranges_zip #include @@ -9213,8 +9214,366 @@ namespace views::__adaptor namespace views = ranges::views; +#if __cpp_lib_ranges_to_container // C++ >= 23 +namespace ranges +{ +/// @cond undocumented +namespace __detail +{ + template + constexpr bool __reservable_container + = sized_range<_Container> + && requires(_Container& __c, range_size_t<_Container> __n) { + __c.reserve(__n); + { __c.capacity() } -> same_as; + { __c.max_size() } -> same_as; + }; + + template + constexpr bool __container_insertable + = requires(_Container& __c, _Ref&& __ref) { + typename _Container::value_type; + requires ( + requires { __c.push_back(std::forward<_Ref>(__ref)); } + || requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); } + ); + }; + + template + constexpr auto + __container_inserter(_Container& __c) + { + if constexpr (requires { __c.push_back(std::declval<_Ref>()); }) + return std::back_inserter(__c); + else + return std::inserter(__c, __c.end()); + } + + template + constexpr bool __toable = requires { + requires (!input_range<_Range> + || convertible_to, + range_value_t<_Cont>>); + }; +} // namespace __detail +/// @endcond + + /// Convert a range to a container. + /** + * @tparam _Cont A container type. + * @param __r A range that models the `input_range` concept. + * @param __args... Arguments to pass to the container constructor. + * @since C++23 + * + * This function converts a range to the `_Cont` type. + * + * For example, `std::ranges::to>(some_view)` + * will convert the view to `std::vector`. + * + * Additional constructor arguments for the container can be supplied after + * the input range argument, e.g. + * `std::ranges::to>>(a_range, an_allocator)`. + */ + template + requires (!view<_Cont>) + constexpr _Cont + to [[nodiscard]] (_Rg&& __r, _Args&&... __args) + { + static_assert(!is_const_v<_Cont> && !is_volatile_v<_Cont>); + static_assert(is_class_v<_Cont>); + + if constexpr (__detail::__toable<_Cont, _Rg>) + { + if constexpr (constructible_from<_Cont, _Rg, _Args...>) + return _Cont(std::forward<_Rg>(__r), + std::forward<_Args>(__args)...); + else if constexpr (constructible_from<_Cont, from_range_t, _Rg, _Args...>) + return _Cont(from_range, std::forward<_Rg>(__r), + std::forward<_Args>(__args)...); + else if constexpr (requires { common_range<_Rg>; + typename __iter_category_t>; + requires derived_from<__iter_category_t>, + input_iterator_tag>; + requires constructible_from<_Cont, iterator_t<_Rg>, + sentinel_t<_Rg>, _Args...>; + }) + return _Cont(ranges::begin(__r), ranges::end(__r), + std::forward<_Args>(__args)...); + else + { + using __detail::__container_insertable; + using __detail::__reservable_container; + using _RefT = range_reference_t<_Rg>; + static_assert(constructible_from<_Cont, _Args...>); + static_assert(__container_insertable<_Cont, _RefT>); + _Cont __c(std::forward<_Args>(__args)...); + if constexpr (sized_range<_Rg> && __reservable_container<_Cont>) + __c.reserve(static_cast>(ranges::size(__r))); + auto __ins = __detail::__container_inserter<_RefT>(__c); + for (auto&& __e : __r) + *__ins++ = std::forward(__e); + return __c; + } + } + else + { + static_assert(input_range>); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3984. ranges::to's recursion branch may be ill-formed + return ranges::to<_Cont>(ref_view(__r) | views::transform( + [](_Elt&& __elem) { + using _ValT = range_value_t<_Cont>; + return ranges::to<_ValT>(std::forward<_Elt>(__elem)); + }), std::forward<_Args>(__args)...); + } + } + +/// @cond undocumented +namespace __detail +{ + template + struct _InputIter + { + using iterator_category = input_iterator_tag; + using value_type = range_value_t<_Rg>; + using difference_type = ptrdiff_t; + using pointer = add_pointer_t>; + using reference = range_reference_t<_Rg>; + reference operator*() const; + pointer operator->() const; + _InputIter& operator++(); + _InputIter operator++(int); + bool operator==(const _InputIter&) const; + }; + +#if 0 + template typename _Cont, typename _Rg, + typename... _Args> + concept __deduce_expr_1 = requires { + _Cont(std::declval<_Rg>(), std::declval<_Args>()...); + }; + + template typename _Cont, typename _Rg, + typename... _Args> + concept __deduce_expr_2 = requires { + _Cont(from_range, std::declval<_Rg>(), std::declval<_Args>()...); + }; + + template typename _Cont, typename _Rg, + typename... _Args> + concept __deduce_expr_3 = requires(_InputIter<_Rg> __i) { + _Cont(std::move(__i), std::move(__i), std::declval<_Args>()...); + }; +#endif + + template typename _Cont, input_range _Rg, + typename... _Args> + using _DeduceExpr1 + = decltype(_Cont(std::declval<_Rg>(), std::declval<_Args>()...)); + + template typename _Cont, input_range _Rg, + typename... _Args> + using _DeduceExpr2 + = decltype(_Cont(from_range, std::declval<_Rg>(), + std::declval<_Args>()...)); + + template typename _Cont, input_range _Rg, + typename... _Args> + using _DeduceExpr3 + = decltype(_Cont(std::declval<_InputIter<_Rg>>(), + std::declval<_InputIter<_Rg>>(), + std::declval<_Args>()...)); + +} // namespace __detail +/// @endcond + + template typename _Cont, input_range _Rg, + typename... _Args> + constexpr auto + to [[nodiscard]] (_Rg&& __r, _Args&&... __args) + { + using __detail::_DeduceExpr1; + using __detail::_DeduceExpr2; + using __detail::_DeduceExpr3; + if constexpr (requires { typename _DeduceExpr1<_Cont, _Rg, _Args...>; }) + return ranges::to<_DeduceExpr1<_Cont, _Rg, _Args...>>( + std::forward<_Rg>(__r), std::forward<_Args>(__args)...); + else if constexpr (requires { typename _DeduceExpr2<_Cont, _Rg, _Args...>; }) + return ranges::to<_DeduceExpr2<_Cont, _Rg, _Args...>>( + std::forward<_Rg>(__r), std::forward<_Args>(__args)...); + else if constexpr (requires { typename _DeduceExpr3<_Cont, _Rg, _Args...>; }) + return ranges::to<_DeduceExpr3<_Cont, _Rg, _Args...>>( + std::forward<_Rg>(__r), std::forward<_Args>(__args)...); + else + static_assert(false); // Cannot deduce container specialization. + } + +/// @cond undocumented +namespace __detail +{ + template + class _ToClosure + : public views::__adaptor::_RangeAdaptorClosure<_ToClosure<_Cont, _Args...>> + { + tuple...> _M_bound_args; + + public: + _ToClosure(_Args&&... __args) + : _M_bound_args(std::forward<_Args>(__args)...) + { } + + // TODO: use explicit object functions ("deducing this"). + + template + constexpr auto + operator()(_Rg&& __r) & + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, _M_bound_args); + } + + template + constexpr auto + operator()(_Rg&& __r) const & + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, _M_bound_args); + } + + template + constexpr auto + operator()(_Rg&& __r) && + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, std::move(_M_bound_args)); + } + + template + constexpr auto + operator()(_Rg&& __r) const && + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, std::move(_M_bound_args)); + } + }; +} // namespace __detail +/// @endcond + + /// ranges::to adaptor for converting a range to a container type + /** + * @tparam _Cont A container type. + * @param __args... Arguments to pass to the container constructor. + * @since C++23 + * + * This range adaptor returns a range adaptor closure object that converts + * a range to the `_Cont` type. + * + * For example, `some_view | std::ranges::to>()` + * will convert the view to `std::vector`. + * + * Additional constructor arguments for the container can be supplied, e.g. + * `r | std::ranges::to>>(an_allocator)`. + */ + template + requires (!view<_Cont>) + constexpr __detail::_ToClosure<_Cont, _Args...> + to [[nodiscard]] (_Args&&... __args) + { return {std::forward<_Args>(__args)...}; } + +/// @cond undocumented +namespace __detail +{ + template typename _Cont, typename... _Args> + class _ToClosure2 + : public views::__adaptor::_RangeAdaptorClosure<_ToClosure2<_Cont, _Args...>> + { + tuple...> _M_bound_args; + + public: + _ToClosure2(_Args&&... __args) + : _M_bound_args(std::forward<_Args>(__args)...) + { } + + // TODO: use explicit object functions ("deducing this"). + + template + constexpr auto + operator()(_Rg&& __r) & + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, _M_bound_args); + } + + template + constexpr auto + operator()(_Rg&& __r) const & + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, _M_bound_args); + } + + template + constexpr auto + operator()(_Rg&& __r) && + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, std::move(_M_bound_args)); + } + + template + constexpr auto + operator()(_Rg&& __r) const && + { + return std::apply([&__r](_Tp&&... __args) { + return ranges::to<_Cont>(std::forward<_Rg>(__r), + std::forward<_Tp>(__args)...); + }, std::move(_M_bound_args)); + } + }; +} // namespace __detail +/// @endcond + + /// ranges::to adaptor for converting a range to a deduced container type. + /** + * @tparam _Cont A container template. + * @param __args... Arguments to pass to the container constructor. + * @since C++23 + * + * This range adaptor returns a range adaptor closure object that converts + * a range to a specialization of the `_Cont` class template. The specific + * specialization of `_Cont` to be used is deduced automatically. + * + * For example, `some_view | std::ranges::to(Alloc{})` + * will convert the view to `std::vector>`, where `T` is the + * view's value type, i.e. `std::ranges::range_value_t`. + * + * Additional constructor arguments for the container can be supplied, e.g. + * `r | std::ranges::to(an_allocator)`. + */ + template typename _Cont, typename... _Args> + constexpr __detail::_ToClosure2<_Cont, _Args...> + to [[nodiscard]] (_Args&&... __args) + { return {std::forward<_Args>(__args)...}; } + +} // namespace ranges +#endif // __cpp_lib_ranges_to_container + _GLIBCXX_END_NAMESPACE_VERSION -} // namespace +} // namespace std #endif // library concepts #endif // C++2a #endif /* _GLIBCXX_RANGES */ diff --git a/libstdc++-v3/testsuite/std/ranges/conv/1.cc b/libstdc++-v3/testsuite/std/ranges/conv/1.cc new file mode 100644 index 00000000000..0032cf32688 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/conv/1.cc @@ -0,0 +1,369 @@ +// { dg-do run { target c++23 } } + +// C++23 26.5.7 Range conversions [range.utility.conv] + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +test_p1206r7_examples() +{ + using Alloc = __gnu_test::uneq_allocator; + const Alloc alloc(303); + const std::map m{{1, "one"}, {2, "two"}, {3, "three"}}; + namespace ranges = std::ranges; + + auto l = std::views::iota(1, 10); + // create a vector with the elements of l + auto vec = ranges::to>(l); // or vector{std::from_range, l}; + //Specify an allocator + auto b = ranges::to>(l, alloc); // or vector{std::from_range, l, alloc}; + //deducing value_type + auto c = ranges::to(l); + // explicit conversion int -> long + auto d = ranges::to>(l); + //Supports converting associative container to sequence containers + auto f = ranges::to(m); + //Supports converting sequence containers to associative ones + auto g = ranges::to(f); + //Pipe syntax + auto g2 = l | ranges::views::take(42) | ranges::to(); + //Pipe syntax with allocator + auto h = l | ranges::views::take(42) | ranges::to(alloc); + //The pipe syntax also support specifying the type and conversions + auto i = l | ranges::views::take(42) | ranges::to>(); + // Nested ranges + std::list> lst = {{0, 1, 2, 3}, {4, 5, 6, 7}}; + auto vec1 = ranges::to>>(lst); + auto vec2 = ranges::to>>(lst); + + VERIFY( vec == std::vector(std::ranges::begin(l), std::ranges::end(l)) ); + static_assert(std::is_same_v>); + VERIFY( b == (std::vector(vec.begin(), vec.end())) ); + VERIFY( b.get_allocator() == alloc ); + static_assert(std::is_same_v>); + VERIFY( c == vec ); + static_assert(std::is_same_v>); + VERIFY( d == std::vector(vec.begin(), vec.end()) ); + VERIFY( g == m ); + static_assert(std::is_same_v>); + VERIFY( g2 == vec ); + static_assert(std::is_same_v>); + VERIFY( h == b ); + VERIFY( h.get_allocator() == alloc ); + VERIFY( i == d ); + static_assert(std::is_same_v>>); + VERIFY( vec1[1][1] == 5 ); + static_assert(std::is_same_v>>); + VERIFY( vec2[1][2] == 6.0 ); +} + +void +test_example_1() +{ + using namespace std; + using ranges::to; + + // Example 1 from C++23 [range.utility.conv.general] + string_view str = "the quick brown fox"; + auto words = views::split(str, ' ') | to>(); + + VERIFY( (is_same_v>) ); + VERIFY( words == vector({"the", "quick", "brown", "fox"}) ); +} + +template +struct Cont1 +{ + template + requires std::constructible_from + Cont1(R&& r, Args&&... args) + : c(r, args...) + { } + + typename C::iterator begin(); + typename C::iterator end(); + + C c; +}; + +void +test_2_1_1() +{ + // (2.1.1) constructible_from + + std::vector v{1, 2, 3, 4}; + auto v2 = std::ranges::to>(v); + static_assert(std::is_same_v); + VERIFY( v2 == v ); + + std::initializer_list il{5, 6, 7, 8}; + v2 = std::ranges::to>(il); + VERIFY( v2 == std::vector(il) ); + + v2 = std::ranges::to>(il, std::allocator{}); + VERIFY( v2 == std::vector(il) ); + + using Alloc = __gnu_test::uneq_allocator; + using V = std::vector; + + V v3({10, 11, 12, 13}, Alloc(14)); + auto v4 = std::ranges::to(v3); + static_assert(std::is_same_v); + VERIFY( v4 == v3 ); + VERIFY( v4.get_allocator() == v3.get_allocator() ); + + auto v5 = std::ranges::to(v3, Alloc(33)); + VERIFY( v5 == v3 ); + VERIFY( v5.get_allocator() == Alloc(33) ); + + auto v6 = std::ranges::to(il, Alloc(44)); + VERIFY( v6 == V(il) ); + VERIFY( v6.get_allocator() == Alloc(44) ); + + auto c = std::ranges::to>(V{1, 2, 3}); + static_assert(std::is_same_v>); + VERIFY( c.c == V({1, 2, 3}) ); + + auto c2 = std::ranges::to>(V{4, 5, 6}, Alloc(55)); + static_assert(std::is_same_v>); + VERIFY( c2.c == V({4, 5, 6}) ); + VERIFY( c2.c.get_allocator() == Alloc(55) ); + + auto c3 = std::ranges::to>(il, Alloc(66)); + static_assert(std::is_same_v>); + VERIFY( c3.c == V(v2.begin(), v2.end()) ); + VERIFY( c3.c.get_allocator() == Alloc(66) ); +} + +template +struct Cont2 +{ + template + requires std::constructible_from + Cont2(std::from_range_t, R&& r, Args&&... args) + : c(r, args...) + { } + + typename C::iterator begin(); + typename C::iterator end(); + + C c; +}; + +void +test_2_1_2() +{ + // (2.1.2) constructible_from + + using Alloc = __gnu_test::uneq_allocator; + using V = std::vector; + auto c = std::ranges::to>(V{1, 2, 3}); + static_assert(std::is_same_v>); + VERIFY( c.c == V({1, 2, 3}) ); + + auto c2 = std::ranges::to>(V{4, 5, 6}, Alloc(7)); + static_assert(std::is_same_v>); + VERIFY( c2.c == V({4, 5, 6}) ); + VERIFY( c2.c.get_allocator() == Alloc(7) ); +} + +template +struct Cont3 +{ + template + requires std::same_as + && std::constructible_from + Cont3(It first, Sent last, Args&&... args) + : c(first, last, args...) + { } + + typename C::iterator begin(); + typename C::iterator end(); + + C c; +}; + +void +test_2_1_3() +{ + // (2.1.3) constructible_from, sentinel_t + + using Alloc = __gnu_test::uneq_allocator; + using V = std::vector; + + std::list l{1u, 2u, 3u}; + auto c = std::ranges::to>(l); + static_assert(std::is_same_v>); + VERIFY( c.c == V(l.begin(), l.end()) ); + + std::list l2{4l, 5l, 6l}; + auto c2 = std::ranges::to>(l2, Alloc(78)); + static_assert(std::is_same_v>); + VERIFY( c2.c == V(l2.begin(), l2.end()) ); + VERIFY( c2.c.get_allocator() == Alloc(78) ); +} + +template +struct Cont4 +{ + using value_type = typename C::value_type; + + // Only support construction with no args or an allocator. + // This forces the use of either push_back or insert to fill the container. + Cont4() { } + Cont4(typename C::allocator_type a) : c(a) { } + + // Required to satisfy range + typename C::iterator begin() { return c.begin(); } + typename C::iterator end() { return c.end(); } + + // Satisfying container-insertable requires either this ... + template + requires UsePushBack + && requires(C& c, T&& t) { c.push_back(std::forward(t)); } + void + push_back(T&& t) + { + c.push_back(std::forward(t)); + used_push_back = true; + } + + // ... or this: + template + typename C::iterator + insert(typename C::iterator, T&& t) + { + used_push_back = false; + return c.insert(c.end(), std::forward(t)); + } + + // Required to satisfy reservable-container + void + reserve(typename C::size_type n) requires requires(C& c) { c.reserve(n); } + { + c.reserve(n); + used_reserve = true; + } + + // Required to satisfy reservable-container + auto size() const { return c.size(); } + + // Required to satisfy reservable-container + auto capacity() const requires requires(C& c) { c.capacity(); } + { return c.capacity(); } + + // Required to satisfy reservable-container + auto max_size() const { return c.max_size(); } + + C c; + bool used_push_back = false; + bool used_reserve = false; +}; + +void +test_2_1_4() +{ + // (2.1.4) constructible_from and + // container-insertable> + + using Alloc = __gnu_test::uneq_allocator; + using V = std::vector; + + std::list l{1u, 2u, 3u}; + auto c = std::ranges::to>(l); + static_assert(std::is_same_v>); + VERIFY( c.c == V(l.begin(), l.end()) ); + VERIFY( c.used_push_back ); + VERIFY( c.used_reserve ); + + std::list l2{4l, 5l, 6l}; + auto c2 = std::ranges::to>(l2, Alloc(78)); + static_assert(std::is_same_v>); + VERIFY( c2.c == V(l2.begin(), l2.end()) ); + VERIFY( c2.c.get_allocator() == Alloc(78) ); + VERIFY( c2.used_push_back ); + VERIFY( c2.used_reserve ); + + using Alloc2 = __gnu_test::uneq_allocator; + using List = std::list; + auto c3 = std::ranges::to>(c.c, Alloc2(99)); + static_assert(std::is_same_v>); + VERIFY( c3.c == List(l.begin(), l.end()) ); + VERIFY( c3.c.get_allocator() == Alloc(99) ); + VERIFY( c3.used_push_back ); + VERIFY( ! c3.used_reserve ); + + auto c4 = std::ranges::to>(c.c, Alloc2(111)); + static_assert(std::is_same_v>); + VERIFY( c4.c == List(l.begin(), l.end()) ); + VERIFY( c4.c.get_allocator() == Alloc(111) ); + VERIFY( ! c4.used_push_back ); + VERIFY( ! c4.used_reserve ); +} + +void +test_2_2() +{ + // (2.2) input_range> + + std::string s1[]{ "one", "two", "three", "four" }; + std::string s2[]{ "V", "VI", "VII", "VIII" }; + std::string s3[]{ "0x09", "0x0a", "0x0b", "0x0c" }; + using R = __gnu_test::test_input_range; + R input_ranges[]{R(s1), R(s2), R(s3)}; + __gnu_test::test_input_range rr(input_ranges); + namespace pmr = std::pmr; + __gnu_test::memory_resource res; +#if _GLIBCXX_USE_CXX11_ABI + auto vvs = std::ranges::to>>(rr, &res); + auto str_alloc = pmr::polymorphic_allocator(&res); +#else + auto vvs = std::ranges::to>>(rr, &res); + auto str_alloc = std::allocator(); +#endif + VERIFY( vvs[1][1] == "VI" ); + VERIFY( vvs[2][2] == "0x0b" ); + VERIFY( vvs[0].get_allocator().resource() == &res ); + VERIFY( vvs[2][2].get_allocator() == str_alloc ); +} + +void +test_lwg3984() +{ + std::vector> v; + auto r = std::views::all(std::move(v)); + auto l = std::ranges::to>>(r); + VERIFY(l.empty()); +} + +void +test_nodiscard() +{ + std::vector v; + std::ranges::to>(v); // { dg-warning "ignoring return" } + std::ranges::to(v); // { dg-warning "ignoring return" } + std::ranges::to>(); // { dg-warning "ignoring return" } + std::ranges::to(); // { dg-warning "ignoring return" } +} + +int main() +{ + test_p1206r7_examples(); + test_example_1(); + test_2_1_1(); + test_2_1_2(); + test_2_1_3(); + test_2_1_4(); + test_2_2(); + test_lwg3984(); + test_nodiscard(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc new file mode 100644 index 00000000000..1e5f6f18408 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc @@ -0,0 +1,24 @@ +// { dg-do compile { target c++23 } } + +// C++23 26.5.7 Range conversions [range.utility.conv] + +#include +#include +#include + +void +test_2_1_5() +{ + // (2.1.5) Otherwise, the program is ill-formed. + + using Alloc = __gnu_test::uneq_allocator; + using Vec = std::vector; + + std::vector v; + (void) std::ranges::to(v, v.get_allocator()); // { dg-error "here" } + + (void) std::ranges::to(Vec{}, 1, 2, 3, 4, 5, 6); // { dg-error "here" } +} + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-prune-output "no matching function" } diff --git a/libstdc++-v3/testsuite/std/ranges/conv/version.cc b/libstdc++-v3/testsuite/std/ranges/conv/version.cc new file mode 100644 index 00000000000..33736807eb4 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/conv/version.cc @@ -0,0 +1,19 @@ +// { dg-do preprocess { target c++23 } } +// { dg-add-options no_pch } + +#include + +#ifndef __cpp_lib_ranges_to_container +# error "Feature test macro for ranges_to_container is missing in " +#elif __cpp_lib_ranges_to_container < 202202L +# error "Feature test macro for ranges_to_container has wrong value in " +#endif + +#undef __cpp_lib_ranges_to_container +#include + +#ifndef __cpp_lib_ranges_to_container +# error "Feature test macro for ranges_to_container is missing in " +#elif __cpp_lib_ranges_to_container < 202202L +# error "Feature test macro for ranges_to_container has wrong value in " +#endif -- 2.11.4.GIT