From: Patrick Palka <ppalka@redhat.com>
To: Jonathan Wakely <jwakely@redhat.com>
Cc: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org
Subject: Re: [committed v2] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055]
Date: Wed, 29 Nov 2023 11:28:17 -0500 (EST) [thread overview]
Message-ID: <cc39b9c5-e3aa-6296-0f37-4a14840b8d6e@idea> (raw)
In-Reply-To: <20231123175247.2451163-1-jwakely@redhat.com>
On Thu, 23 Nov 2023, Jonathan Wakely wrote:
> Here's the finished version of the std::ranges::to patch, which I've
> pushed to trunk.
>
> Tested x86_64-linux.
>
> -- >8 --
>
> 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 <ranges>, 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 ++++++++++++++++++
> .../testsuite/std/ranges/conv/2_neg.cc | 24 ++
> .../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 <bits/stl_iterator.h>
> #include <ext/numeric_traits.h>
> #include <bits/max_size_type.h>
> +#include <bits/version.h>
>
> #ifdef __cpp_lib_concepts
> namespace std _GLIBCXX_VISIBILITY(default)
> @@ -1056,8 +1057,13 @@ namespace ranges
> using borrowed_iterator_t = __conditional_t<borrowed_range<_Range>,
> 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;
> };
> };
>
> @@ -1683,6 +1685,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 = {
> 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
> +// 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_to_underlying) && defined(__glibcxx_want_to_underlying) */
> -#undef __glibcxx_want_to_underlying
> +#endif /* !defined(__cpp_lib_ranges_to_container) && defined(__glibcxx_want_ranges_to_container) */
> +#undef __glibcxx_want_ranges_to_container
>
> -// 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
> -# endif
> -# endif
> -#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */
> -#undef __glibcxx_want_unreachable
> -
> -// from version.def line 1459
> +// from version.def line 1461
> #if !defined(__cpp_lib_ranges_zip)
> # if (__cplusplus >= 202100L)
> # define __glibcxx_ranges_zip 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
> 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 <bits/version.h>
>
> @@ -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<typename _Container>
> + constexpr bool __reservable_container
> + = sized_range<_Container>
> + && requires(_Container& __c, range_size_t<_Container> __n) {
> + __c.reserve(__n);
> + { __c.capacity() } -> same_as<decltype(__n)>;
> + { __c.max_size() } -> same_as<decltype(__n)>;
> + };
> +
> + template<typename _Container, typename _Ref>
> + 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<typename _Ref, typename _Container>
> + 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<typename _Cont, typename _Range>
> + constexpr bool __toable = requires {
> + requires (!input_range<_Range>
It seems this sholud be input_range<_Cont>?
> + || convertible_to<range_reference_t<_Range>,
> + 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<std::vector<int>>(some_view)`
> + * will convert the view to `std::vector<int>`.
> + *
> + * Additional constructor arguments for the container can be supplied after
> + * the input range argument, e.g.
> + * `std::ranges::to<std::vector<int, Alloc<int>>>(a_range, an_allocator)`.
> + */
> + template<typename _Cont, input_range _Rg, typename... _Args>
> + 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>;
Missing 'requires' before common_range?
> + typename __iter_category_t<iterator_t<_Rg>>;
> + requires derived_from<__iter_category_t<iterator_t<_Rg>>,
> + 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<range_size_t<_Cont>>(ranges::size(__r)));
> + auto __ins = __detail::__container_inserter<_RefT>(__c);
> + for (auto&& __e : __r)
> + *__ins++ = std::forward<decltype(__e)>(__e);
> + return __c;
> + }
> + }
> + else
> + {
> + static_assert(input_range<range_reference_t<_Rg>>);
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 3984. ranges::to's recursion branch may be ill-formed
> + return ranges::to<_Cont>(ref_view(__r) | views::transform(
> + []<typename _Elt>(_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<typename _Rg>
> + 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<range_reference_t<_Rg>>;
> + 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<template<typename...> typename _Cont, typename _Rg,
> + typename... _Args>
> + concept __deduce_expr_1 = requires {
> + _Cont(std::declval<_Rg>(), std::declval<_Args>()...);
> + };
> +
> + template<template<typename...> typename _Cont, typename _Rg,
> + typename... _Args>
> + concept __deduce_expr_2 = requires {
> + _Cont(from_range, std::declval<_Rg>(), std::declval<_Args>()...);
> + };
> +
> + template<template<typename...> 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<template<typename...> typename _Cont, input_range _Rg,
> + typename... _Args>
> + using _DeduceExpr1
> + = decltype(_Cont(std::declval<_Rg>(), std::declval<_Args>()...));
> +
> + template<template<typename...> typename _Cont, input_range _Rg,
> + typename... _Args>
> + using _DeduceExpr2
> + = decltype(_Cont(from_range, std::declval<_Rg>(),
> + std::declval<_Args>()...));
> +
> + template<template<typename...> 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<template<typename...> 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<typename _Cont, typename... _Args>
> + class _ToClosure
> + : public views::__adaptor::_RangeAdaptorClosure<_ToClosure<_Cont, _Args...>>
> + {
> + tuple<decay_t<_Args>...> _M_bound_args;
> +
> + public:
> + _ToClosure(_Args&&... __args)
Missing constexpr?
> + : _M_bound_args(std::forward<_Args>(__args)...)
> + { }
> +
> + // TODO: use explicit object functions ("deducing this").
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) &
> + {
> + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> + return ranges::to<_Cont>(std::forward<_Rg>(__r),
> + std::forward<_Tp>(__args)...);
> + }, _M_bound_args);
> + }
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) const &
> + {
> + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> + return ranges::to<_Cont>(std::forward<_Rg>(__r),
> + std::forward<_Tp>(__args)...);
> + }, _M_bound_args);
> + }
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) &&
> + {
> + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> + return ranges::to<_Cont>(std::forward<_Rg>(__r),
> + std::forward<_Tp>(__args)...);
> + }, std::move(_M_bound_args));
> + }
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) const &&
> + {
> + return std::apply([&__r]<typename... _Tp>(_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<std::vector<int>>()`
> + * will convert the view to `std::vector<int>`.
> + *
> + * Additional constructor arguments for the container can be supplied, e.g.
> + * `r | std::ranges::to<std::vector<int, Alloc<int>>>(an_allocator)`.
> + */
> + template<typename _Cont, typename... _Args>
> + requires (!view<_Cont>)
> + constexpr __detail::_ToClosure<_Cont, _Args...>
> + to [[nodiscard]] (_Args&&... __args)
> + { return {std::forward<_Args>(__args)...}; }
> +
> +/// @cond undocumented
> +namespace __detail
> +{
> + template<template<typename...> typename _Cont, typename... _Args>
> + class _ToClosure2
> + : public views::__adaptor::_RangeAdaptorClosure<_ToClosure2<_Cont, _Args...>>
> + {
> + tuple<decay_t<_Args>...> _M_bound_args;
> +
> + public:
> + _ToClosure2(_Args&&... __args)
Same here.
Would it be possible to use _RangeAdaptor instead of _RangeAdaptorClosure to
leverage the existing partial application/forwarding code? IIUC we'd need to
allow omitting the _S_arity member to mean accept any number of arguments.
I can work on that if anything.
> + : _M_bound_args(std::forward<_Args>(__args)...)
> + { }
> +
> + // TODO: use explicit object functions ("deducing this").
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) &
> + {
> + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> + return ranges::to<_Cont>(std::forward<_Rg>(__r),
> + std::forward<_Tp>(__args)...);
> + }, _M_bound_args);
> + }
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) const &
> + {
> + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> + return ranges::to<_Cont>(std::forward<_Rg>(__r),
> + std::forward<_Tp>(__args)...);
> + }, _M_bound_args);
> + }
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) &&
> + {
> + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> + return ranges::to<_Cont>(std::forward<_Rg>(__r),
> + std::forward<_Tp>(__args)...);
> + }, std::move(_M_bound_args));
> + }
> +
> + template<typename _Rg>
> + constexpr auto
> + operator()(_Rg&& __r) const &&
> + {
> + return std::apply([&__r]<typename... _Tp>(_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<std::vector>(Alloc<int>{})`
> + * will convert the view to `std::vector<T, Alloc<T>>`, where `T` is the
> + * view's value type, i.e. `std::ranges::range_value_t<decltype(some_view)>`.
> + *
> + * Additional constructor arguments for the container can be supplied, e.g.
> + * `r | std::ranges::to<std::vector>(an_allocator)`.
> + */
> + template<template<typename...> 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 <ranges>
> +#include <vector>
> +#include <string>
> +#include <deque>
> +#include <list>
> +#include <forward_list>
> +#include <map>
> +#include <testsuite_hooks.h>
> +#include <testsuite_allocator.h>
> +#include <testsuite_iterators.h>
> +
> +void
> +test_p1206r7_examples()
> +{
> + using Alloc = __gnu_test::uneq_allocator<int>;
> + const Alloc alloc(303);
> + const std::map<int, const char*> 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<std::vector<int>>(l); // or vector{std::from_range, l};
> + //Specify an allocator
> + auto b = ranges::to<std::vector<int, Alloc>>(l, alloc); // or vector{std::from_range, l, alloc};
> + //deducing value_type
> + auto c = ranges::to<std::vector>(l);
> + // explicit conversion int -> long
> + auto d = ranges::to<std::vector<long>>(l);
> + //Supports converting associative container to sequence containers
> + auto f = ranges::to<std::vector>(m);
> + //Supports converting sequence containers to associative ones
> + auto g = ranges::to<std::map>(f);
> + //Pipe syntax
> + auto g2 = l | ranges::views::take(42) | ranges::to<std::vector>();
> + //Pipe syntax with allocator
> + auto h = l | ranges::views::take(42) | ranges::to<std::vector>(alloc);
> + //The pipe syntax also support specifying the type and conversions
> + auto i = l | ranges::views::take(42) | ranges::to<std::vector<long>>();
> + // Nested ranges
> + std::list<std::forward_list<int>> lst = {{0, 1, 2, 3}, {4, 5, 6, 7}};
> + auto vec1 = ranges::to<std::vector<std::vector<int>>>(lst);
> + auto vec2 = ranges::to<std::vector<std::deque<double>>>(lst);
> +
> + VERIFY( vec == std::vector<int>(std::ranges::begin(l), std::ranges::end(l)) );
> + static_assert(std::is_same_v<decltype(b), std::vector<int, Alloc>>);
> + VERIFY( b == (std::vector<int, Alloc>(vec.begin(), vec.end())) );
> + VERIFY( b.get_allocator() == alloc );
> + static_assert(std::is_same_v<decltype(c), std::vector<int>>);
> + VERIFY( c == vec );
> + static_assert(std::is_same_v<decltype(d), std::vector<long>>);
> + VERIFY( d == std::vector<long>(vec.begin(), vec.end()) );
> + VERIFY( g == m );
> + static_assert(std::is_same_v<decltype(g2), std::vector<int>>);
> + VERIFY( g2 == vec );
> + static_assert(std::is_same_v<decltype(h), std::vector<int, Alloc>>);
> + VERIFY( h == b );
> + VERIFY( h.get_allocator() == alloc );
> + VERIFY( i == d );
> + static_assert(std::is_same_v<decltype(vec1), std::vector<std::vector<int>>>);
> + VERIFY( vec1[1][1] == 5 );
> + static_assert(std::is_same_v<decltype(vec2), std::vector<std::deque<double>>>);
> + 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<vector<string>>();
> +
> + VERIFY( (is_same_v<decltype(words), vector<string>>) );
> + VERIFY( words == vector<string>({"the", "quick", "brown", "fox"}) );
> +}
> +
> +template<typename C>
> +struct Cont1
> +{
> + template<typename R, typename... Args>
> + requires std::constructible_from<C, R&, Args&...>
> + 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<C, R, Args...>
> +
> + std::vector<int> v{1, 2, 3, 4};
> + auto v2 = std::ranges::to<std::vector<int>>(v);
> + static_assert(std::is_same_v<decltype(v2), decltype(v)>);
> + VERIFY( v2 == v );
> +
> + std::initializer_list<int> il{5, 6, 7, 8};
> + v2 = std::ranges::to<std::vector<int>>(il);
> + VERIFY( v2 == std::vector<int>(il) );
> +
> + v2 = std::ranges::to<std::vector<int>>(il, std::allocator<int>{});
> + VERIFY( v2 == std::vector<int>(il) );
> +
> + using Alloc = __gnu_test::uneq_allocator<int>;
> + using V = std::vector<int, Alloc>;
> +
> + V v3({10, 11, 12, 13}, Alloc(14));
> + auto v4 = std::ranges::to<V>(v3);
> + static_assert(std::is_same_v<decltype(v4), V>);
> + VERIFY( v4 == v3 );
> + VERIFY( v4.get_allocator() == v3.get_allocator() );
> +
> + auto v5 = std::ranges::to<V>(v3, Alloc(33));
> + VERIFY( v5 == v3 );
> + VERIFY( v5.get_allocator() == Alloc(33) );
> +
> + auto v6 = std::ranges::to<V>(il, Alloc(44));
> + VERIFY( v6 == V(il) );
> + VERIFY( v6.get_allocator() == Alloc(44) );
> +
> + auto c = std::ranges::to<Cont1<V>>(V{1, 2, 3});
> + static_assert(std::is_same_v<decltype(c), Cont1<V>>);
> + VERIFY( c.c == V({1, 2, 3}) );
> +
> + auto c2 = std::ranges::to<Cont1<V>>(V{4, 5, 6}, Alloc(55));
> + static_assert(std::is_same_v<decltype(c2), Cont1<V>>);
> + VERIFY( c2.c == V({4, 5, 6}) );
> + VERIFY( c2.c.get_allocator() == Alloc(55) );
> +
> + auto c3 = std::ranges::to<Cont1<V>>(il, Alloc(66));
> + static_assert(std::is_same_v<decltype(c2), Cont1<V>>);
> + VERIFY( c3.c == V(v2.begin(), v2.end()) );
> + VERIFY( c3.c.get_allocator() == Alloc(66) );
> +}
> +
> +template<typename C>
> +struct Cont2
> +{
> + template<typename R, typename... Args>
> + requires std::constructible_from<C, R&, Args&...>
> + 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<C, from_range_t, R, Args...>
> +
> + using Alloc = __gnu_test::uneq_allocator<int>;
> + using V = std::vector<int, Alloc>;
> + auto c = std::ranges::to<Cont2<V>>(V{1, 2, 3});
> + static_assert(std::is_same_v<decltype(c), Cont2<V>>);
> + VERIFY( c.c == V({1, 2, 3}) );
> +
> + auto c2 = std::ranges::to<Cont2<V>>(V{4, 5, 6}, Alloc(7));
> + static_assert(std::is_same_v<decltype(c2), Cont2<V>>);
> + VERIFY( c2.c == V({4, 5, 6}) );
> + VERIFY( c2.c.get_allocator() == Alloc(7) );
> +}
> +
> +template<typename C>
> +struct Cont3
> +{
> + template<typename It, typename Sent, typename... Args>
> + requires std::same_as<It, Sent>
> + && std::constructible_from<C, It&, Sent&, Args&...>
> + 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<C, iterator_t<R>, sentinel_t<R<, Args...>
> +
> + using Alloc = __gnu_test::uneq_allocator<int>;
> + using V = std::vector<int, Alloc>;
> +
> + std::list<unsigned> l{1u, 2u, 3u};
> + auto c = std::ranges::to<Cont3<V>>(l);
> + static_assert(std::is_same_v<decltype(c), Cont3<V>>);
> + VERIFY( c.c == V(l.begin(), l.end()) );
> +
> + std::list<long> l2{4l, 5l, 6l};
> + auto c2 = std::ranges::to<Cont3<V>>(l2, Alloc(78));
> + static_assert(std::is_same_v<decltype(c2), Cont3<V>>);
> + VERIFY( c2.c == V(l2.begin(), l2.end()) );
> + VERIFY( c2.c.get_allocator() == Alloc(78) );
> +}
> +
> +template<typename C, bool UsePushBack = true>
> +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<typename T>
> + requires UsePushBack
> + && requires(C& c, T&& t) { c.push_back(std::forward<T>(t)); }
> + void
> + push_back(T&& t)
> + {
> + c.push_back(std::forward<T>(t));
> + used_push_back = true;
> + }
> +
> + // ... or this:
> + template<typename T>
> + typename C::iterator
> + insert(typename C::iterator, T&& t)
> + {
> + used_push_back = false;
> + return c.insert(c.end(), std::forward<T>(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<C, Args...> and
> + // container-insertable<C, range_reference_t<R>>
> +
> + using Alloc = __gnu_test::uneq_allocator<int>;
> + using V = std::vector<int, Alloc>;
> +
> + std::list<unsigned> l{1u, 2u, 3u};
> + auto c = std::ranges::to<Cont4<V>>(l);
> + static_assert(std::is_same_v<decltype(c), Cont4<V>>);
> + VERIFY( c.c == V(l.begin(), l.end()) );
> + VERIFY( c.used_push_back );
> + VERIFY( c.used_reserve );
> +
> + std::list<long> l2{4l, 5l, 6l};
> + auto c2 = std::ranges::to<Cont4<V>>(l2, Alloc(78));
> + static_assert(std::is_same_v<decltype(c2), Cont4<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<short>;
> + using List = std::list<short, Alloc2>;
> + auto c3 = std::ranges::to<Cont4<List>>(c.c, Alloc2(99));
> + static_assert(std::is_same_v<decltype(c3), Cont4<List>>);
> + 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<Cont4<List, false>>(c.c, Alloc2(111));
> + static_assert(std::is_same_v<decltype(c4), Cont4<List, false>>);
> + 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<range_reference_t<R>>
> +
> + 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<std::string>;
> + R input_ranges[]{R(s1), R(s2), R(s3)};
> + __gnu_test::test_input_range<R> rr(input_ranges);
> + namespace pmr = std::pmr;
> + __gnu_test::memory_resource res;
> +#if _GLIBCXX_USE_CXX11_ABI
> + auto vvs = std::ranges::to<pmr::vector<pmr::vector<pmr::string>>>(rr, &res);
> + auto str_alloc = pmr::polymorphic_allocator<char>(&res);
> +#else
> + auto vvs = std::ranges::to<pmr::vector<pmr::vector<std::string>>>(rr, &res);
> + auto str_alloc = std::allocator<char>();
> +#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<std::vector<int>> v;
> + auto r = std::views::all(std::move(v));
> + auto l = std::ranges::to<std::list<std::list<int>>>(r);
> + VERIFY(l.empty());
> +}
> +
> +void
> +test_nodiscard()
> +{
> + std::vector<int> v;
> + std::ranges::to<std::vector<long>>(v); // { dg-warning "ignoring return" }
> + std::ranges::to<std::vector>(v); // { dg-warning "ignoring return" }
> + std::ranges::to<std::vector<long>>(); // { dg-warning "ignoring return" }
> + std::ranges::to<std::vector>(); // { 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 <ranges>
> +#include <vector>
> +#include <testsuite_allocator.h>
> +
> +void
> +test_2_1_5()
> +{
> + // (2.1.5) Otherwise, the program is ill-formed.
> +
> + using Alloc = __gnu_test::uneq_allocator<int>;
> + using Vec = std::vector<int, Alloc>;
> +
> + std::vector<int> v;
> + (void) std::ranges::to<Vec>(v, v.get_allocator()); // { dg-error "here" }
> +
> + (void) std::ranges::to<Vec>(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 <ranges>
> +
> +#ifndef __cpp_lib_ranges_to_container
> +# error "Feature test macro for ranges_to_container is missing in <ranges>"
> +#elif __cpp_lib_ranges_to_container < 202202L
> +# error "Feature test macro for ranges_to_container has wrong value in <ranges>"
> +#endif
> +
> +#undef __cpp_lib_ranges_to_container
> +#include <version>
> +
> +#ifndef __cpp_lib_ranges_to_container
> +# error "Feature test macro for ranges_to_container is missing in <version>"
> +#elif __cpp_lib_ranges_to_container < 202202L
> +# error "Feature test macro for ranges_to_container has wrong value in <version>"
> +#endif
> --
> 2.42.0
>
>
next prev parent reply other threads:[~2023-11-29 16:28 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-17 15:49 [PATCH] " Jonathan Wakely
2023-11-23 17:51 ` [committed v2] " Jonathan Wakely
2023-11-28 0:02 ` Hans-Peter Nilsson
2023-11-29 16:28 ` Patrick Palka [this message]
2023-11-30 15:49 ` Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cc39b9c5-e3aa-6296-0f37-4a14840b8d6e@idea \
--to=ppalka@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jwakely@redhat.com \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).