From 20828a812822f3009c3fe8a15d3db9160819b7de Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 8 Oct 2024 21:15:18 +0100 Subject: [PATCH] libstdc++: Add P1206R7 from_range members to container adaptors [PR111055] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is another piece of P1206R7, adding new members to std::stack, std::queue, and std::priority_queue. PR libstdc++/111055 libstdc++-v3/ChangeLog: * include/bits/stl_queue.h (queue(from_range_t, _Rg&&)) (queue(from_range_t, _Rg&&, const _Alloc&), push_range): Define. (priority_queue(from_range_t, R&&, const Compare&)) (push_range): Define. * include/bits/stl_stack.h (stack(from_range_t, R&&)) (stack(from_range_t, R&&, const Alloc&), push_range): Define. * testsuite/util/testsuite_iterators.h (test_range_nocopy): Define. * testsuite/23_containers/priority_queue/cons_from_range.cc: New test. * testsuite/23_containers/priority_queue/members/push_range.cc: New test. * testsuite/23_containers/queue/cons_from_range.cc: New test. * testsuite/23_containers/queue/members/push_range.cc: New test. * testsuite/23_containers/stack/cons_from_range.cc: New test. * testsuite/23_containers/stack/members/push_range.cc: New test. Co-authored-by: Tomasz Kamiński Signed-off-by: Tomasz Kamiński --- libstdc++-v3/include/bits/stl_queue.h | 102 ++++++++++++++++ libstdc++-v3/include/bits/stl_stack.h | 46 ++++++++ .../priority_queue/cons_from_range.cc | 111 ++++++++++++++++++ .../priority_queue/members/push_range.cc | 86 ++++++++++++++ .../23_containers/queue/cons_from_range.cc | 88 ++++++++++++++ .../23_containers/queue/members/push_range.cc | 73 ++++++++++++ .../23_containers/stack/cons_from_range.cc | 89 ++++++++++++++ .../23_containers/stack/members/push_range.cc | 74 ++++++++++++ .../testsuite/util/testsuite_iterators.h | 11 ++ 9 files changed, 680 insertions(+) create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/cons_from_range.cc create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/members/push_range.cc create mode 100644 libstdc++-v3/testsuite/23_containers/queue/cons_from_range.cc create mode 100644 libstdc++-v3/testsuite/23_containers/queue/members/push_range.cc create mode 100644 libstdc++-v3/testsuite/23_containers/stack/cons_from_range.cc create mode 100644 libstdc++-v3/testsuite/23_containers/stack/members/push_range.cc diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h index 627d5e4e63b..2a4b62918a0 100644 --- a/libstdc++-v3/include/bits/stl_queue.h +++ b/libstdc++-v3/include/bits/stl_queue.h @@ -61,6 +61,10 @@ #if __cplusplus >= 201103L # include #endif +#if __glibcxx_ranges_to_container // C++ >= 23 +# include // ranges::to +# include // ranges::copy +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -209,6 +213,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : c(__first, __last, __a) { } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a queue from a range. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + queue(from_range_t, _Rg&& __rg) + : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))) + { } + + /** + * @brief Construct a queue from a range. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg, + typename _Alloc> + queue(from_range_t, _Rg&& __rg, const _Alloc& __a) + : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)) + { } +#endif + /** * Returns true if the %queue is empty. */ @@ -301,6 +326,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + void + push_range(_Rg&& __rg) + { + if constexpr (requires { c.append_range(std::forward<_Rg>(__rg)); }) + c.append_range(std::forward<_Rg>(__rg)); + else + ranges::copy(__rg, std::back_inserter(c)); + } +#endif + /** * @brief Removes first element. * @@ -359,6 +396,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION queue(_InputIterator, _InputIterator, _Allocator) -> queue<_ValT, deque<_ValT, _Allocator>>; #endif + +#if __glibcxx_ranges_to_container // C++ >= 23 + template + queue(from_range_t, _Rg&&) -> queue>; + + template + queue(from_range_t, _Rg&&, _Alloc) + -> queue, + deque, _Alloc>>; +#endif #endif /** @@ -719,6 +766,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a priority_queue from a range. + * @since C++23 + * + * @{ + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + priority_queue(from_range_t, _Rg&& __rg, + const _Compare& __x = _Compare()) + : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))), comp(__x) + { std::make_heap(c.begin(), c.end(), comp); } + + template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc> + priority_queue(from_range_t, _Rg&& __rg, const _Compare& __x, + const _Alloc& __a) + : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp(__x) + { std::make_heap(c.begin(), c.end(), comp); } + + template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc> + priority_queue(from_range_t, _Rg&& __rg, const _Alloc& __a) + : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp() + { std::make_heap(c.begin(), c.end(), comp); } + /// @} +#endif + /** * Returns true if the %queue is empty. */ @@ -776,6 +849,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + void + push_range(_Rg&& __rg) + { + if constexpr (requires { c.append_range(std::forward<_Rg>(__rg)); }) + c.append_range(std::forward<_Rg>(__rg)); + else + ranges::copy(__rg, std::back_inserter(c)); + std::make_heap(c.begin(), c.end(), comp); + } +#endif + /** * @brief Removes first element. * @@ -837,6 +923,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename = _RequireNotAllocator<_Container>> priority_queue(_Compare, _Container, _Allocator) -> priority_queue; + +#if __glibcxx_ranges_to_container // C++ >= 23 + template>, + __allocator_like _Alloc = std::allocator>> + priority_queue(from_range_t, _Rg&&, _Compare = _Compare(), + _Alloc = _Alloc()) + -> priority_queue, + vector, _Alloc>, + _Compare>; + + template + priority_queue(from_range_t, _Rg&&, _Alloc) + -> priority_queue, + vector, _Alloc>>; +#endif #endif // No equality/comparison operators are provided for priority_queue. diff --git a/libstdc++-v3/include/bits/stl_stack.h b/libstdc++-v3/include/bits/stl_stack.h index ce749f2f8b9..2a274bf4c3a 100644 --- a/libstdc++-v3/include/bits/stl_stack.h +++ b/libstdc++-v3/include/bits/stl_stack.h @@ -61,6 +61,10 @@ #if __cplusplus >= 201103L # include #endif +#if __glibcxx_ranges_to_container // C++ >= 23 +# include // ranges::to +# include // ranges::copy +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -177,6 +181,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : c(__first, __last) { } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a stack from a range. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + stack(from_range_t, _Rg&& __rg) + : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))) + { } + + /** + * @brief Construct a stack from a range. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg, + typename _Alloc> + stack(from_range_t, _Rg&& __rg, const _Alloc& __a) + : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)) + { } +#endif template> explicit @@ -276,6 +300,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + void + push_range(_Rg&& __rg) + { + if constexpr (requires { c.append_range(std::forward<_Rg>(__rg)); }) + c.append_range(std::forward<_Rg>(__rg)); + else + ranges::copy(__rg, std::back_inserter(c)); + } +#endif + /** * @brief Removes first element. * @@ -334,6 +370,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION stack(_InputIterator, _InputIterator, _Allocator) -> stack<_ValT, deque<_ValT, _Allocator>>; #endif + +#if __glibcxx_ranges_to_container // C++ >= 23 + template + stack(from_range_t, _Rg&&) -> stack>; + + template + stack(from_range_t, _Rg&&, _Alloc) + -> stack, + deque, _Alloc>>; +#endif #endif /** diff --git a/libstdc++-v3/testsuite/23_containers/priority_queue/cons_from_range.cc b/libstdc++-v3/testsuite/23_containers/priority_queue/cons_from_range.cc new file mode 100644 index 00000000000..a7ff3e3b037 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/priority_queue/cons_from_range.cc @@ -0,0 +1,111 @@ +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include +#include +#include + +struct Gt { + template + bool operator()(T const& l, U const & r) { + return l > r; + } +}; + +void +test_deduction_guide(long* p) +{ + __gnu_test::test_input_range r(p, p); + std::priority_queue pq(std::from_range, r); + static_assert(std::is_same_v>); + + Gt cmp; + std::priority_queue pq3(std::from_range, r, cmp); + static_assert(std::is_same_v, Gt>>); + + using Alloc = __gnu_test::SimpleAllocator; + Alloc alloc; + std::priority_queue pq2(std::from_range, r, alloc); + static_assert(std::is_same_v>>); + + std::priority_queue pq4(std::from_range, r, cmp, alloc); + static_assert(std::is_same_v, Gt>>); +} + +template> +constexpr void +do_test(Cmp cmp = Cmp()) +{ + // The queue's value_type. + using V = typename Cont::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [&](std::priority_queue& l, std::span r) { + if (l.size() != r.size()) + return false; + + std::vector s(r.begin(), r.end()); + std::ranges::sort(s, cmp); + for (auto const& v : s | std::views::reverse) { + if (v != l.top()) + return false; + l.pop(); + } + return true; + }; + + std::priority_queue pq0(std::from_range, Range(a, a+0)); + VERIFY( pq0.empty() ); + + std::priority_queue pq4(std::from_range, Range(a, a+4), cmp); + VERIFY( eq(pq4, {a, 4}) ); + + typename Cont::allocator_type alloc; + std::priority_queue pq7(std::from_range, Range(a, a+7), alloc); + VERIFY( eq(pq7, {a, 7}) ); + + std::priority_queue pq9(std::from_range, Range(a, a+9), cmp, alloc); + VERIFY( eq(pq9, {a, 9}) ); +} + +template> +struct NoFromRangeCont : std::vector +{ + NoFromRangeCont() = default; + NoFromRangeCont(const Alloc& a) : std::vector(a) {} +}; + +template +void +do_test_c() +{ + do_test>(); + do_test>>(); + do_test, Gt>(); + do_test>, Gt>(); + do_test>(); + do_test>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_c>(); + do_test_c>(); + do_test_c>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/priority_queue/members/push_range.cc b/libstdc++-v3/testsuite/23_containers/priority_queue/members/push_range.cc new file mode 100644 index 00000000000..7ab7ad19935 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/priority_queue/members/push_range.cc @@ -0,0 +1,86 @@ +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include +#include +#include + +struct Gt { + template + bool operator()(T const& l, U const & r) { + return l > r; + } +}; + +template> +constexpr void +do_test(Cmp cmp = Cmp()) +{ + // The queue's value_type. + using V = typename Cont::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [&](std::priority_queue l, std::span r) { + if (l.size() != r.size()) + return false; + + std::vector s(r.begin(), r.end()); + std::ranges::sort(s, cmp); + for (auto const& v : s | std::views::reverse) { + if (v != l.top()) + return false; + l.pop(); + } + return true; + }; + + std::priority_queue pq(std::from_range, Range(a, a+0)); + pq.push_range(Range(a, a+0)); + VERIFY( pq.empty() ); + + pq.push_range(Range(a, a+4)); + VERIFY( eq(pq, {a, 4}) ); + + pq.push_range(Range(a+4, a+9)); + VERIFY( eq(pq, {a, 9}) ); +} + +template> +struct NoAppendRangeCont : std::vector +{ + template + void append_range(R&&) = delete; +}; + +template +void +do_test_c() +{ + do_test>(); + do_test, Gt>(); + do_test>(); + do_test>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_c>(); + do_test_c>(); + do_test_c>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/queue/cons_from_range.cc b/libstdc++-v3/testsuite/23_containers/queue/cons_from_range.cc new file mode 100644 index 00000000000..c21f52cb1e2 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/queue/cons_from_range.cc @@ -0,0 +1,88 @@ +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include +#include + +void +test_deduction_guide(long* p) +{ + __gnu_test::test_input_range r(p, p); + std::queue q(std::from_range, r); + static_assert(std::is_same_v>); + + using Alloc = __gnu_test::SimpleAllocator; + Alloc alloc; + std::queue q2(std::from_range, r, alloc); + static_assert(std::is_same_v>>); +} + +template +constexpr void +do_test() +{ + // The queue's value_type. + using V = typename Cont::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](std::queue& l, std::span r) { + if (l.size() != r.size()) + return false; + for (auto const& v : r) { + if (v != l.front()) + return false; + l.pop(); + } + return true; + }; + + std::queue q0(std::from_range, Range(a, a+0)); + VERIFY( q0.empty() ); + + std::queue q4(std::from_range, Range(a, a+4)); + VERIFY( eq(q4, {a, 4}) ); + + typename Cont::allocator_type alloc; + std::queue q9(std::from_range, Range(a, a+9), alloc); + VERIFY( eq(q9, {a, 9}) ); +} + +template> +struct NoFromRangeCont : std::deque +{ + NoFromRangeCont() = default; + NoFromRangeCont(const Alloc& a) : std::deque(a) {} +}; + +template +void +do_test_c() +{ + do_test>(); + do_test>>(); + do_test>(); + do_test>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_c>(); + do_test_c>(); + do_test_c>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/queue/members/push_range.cc b/libstdc++-v3/testsuite/23_containers/queue/members/push_range.cc new file mode 100644 index 00000000000..bf82b869c1b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/queue/members/push_range.cc @@ -0,0 +1,73 @@ +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include + +template +constexpr void +do_test() +{ + // The queue's value_type. + using V = typename Cont::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](std::queue l, std::span r) { + if (l.size() != r.size()) + return false; + for (auto const& v : r) { + if (v != l.front()) + return false; + l.pop(); + } + return true; + }; + + std::queue q; + q.push_range(Range(a, a+0)); + VERIFY( q.empty() ); + + q.push_range(Range(a, a+4)); + VERIFY( eq(q, {a, 4}) ); + + q.push_range(Range(a+4, a+9)); + VERIFY( eq(q, {a, 9}) ); +} + +template> +struct NoAppendRangeCont : std::deque +{ + template + void append_range(R&&) = delete; +}; + +template +void +do_test_c() +{ + do_test>(); + do_test>(); + do_test>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_c>(); + do_test_c>(); + do_test_c>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/stack/cons_from_range.cc b/libstdc++-v3/testsuite/23_containers/stack/cons_from_range.cc new file mode 100644 index 00000000000..e957d0c4450 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/stack/cons_from_range.cc @@ -0,0 +1,89 @@ +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include +#include +#include + +void +test_deduction_guide(long* p) +{ + __gnu_test::test_input_range r(p, p); + std::stack s(std::from_range, r); + static_assert(std::is_same_v>); + + using Alloc = __gnu_test::SimpleAllocator; + Alloc alloc; + std::stack s2(std::from_range, r, alloc); + static_assert(std::is_same_v>>); +} + +template +constexpr void +do_test() +{ + // The stack's value_type. + using V = typename Cont::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](std::stack& l, std::span r) { + if (l.size() != r.size()) + return false; + for (auto const& v : r | std::views::reverse) { + if (v != l.top()) + return false; + l.pop(); + } + return true; + }; + + std::stack s0(std::from_range, Range(a, a+0)); + VERIFY( s0.empty() ); + + std::stack s4(std::from_range, Range(a, a+4)); + VERIFY( eq(s4, {a, 4}) ); + + typename Cont::allocator_type alloc; + std::stack s9(std::from_range, Range(a, a+9), alloc); + VERIFY( eq(s9, {a, 9}) ); +} + +template> +struct NoFromRangeCont : std::deque +{ + NoFromRangeCont() = default; + NoFromRangeCont(const Alloc& a) : std::deque(a) {} +}; + +template +void +do_test_c() +{ + do_test>(); + do_test>>(); + do_test>(); + do_test>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_c>(); + do_test_c>(); + do_test_c>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/stack/members/push_range.cc b/libstdc++-v3/testsuite/23_containers/stack/members/push_range.cc new file mode 100644 index 00000000000..78b826a7028 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/stack/members/push_range.cc @@ -0,0 +1,74 @@ +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include +#include + +template +constexpr void +do_test() +{ + // The stack's value_type. + using V = typename Cont::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](std::stack l, std::span r) { + if (l.size() != r.size()) + return false; + for (auto const& v : r | std::views::reverse) { + if (v != l.top()) + return false; + l.pop(); + } + return true; + }; + + std::stack s; + s.push_range(Range(a, a+0)); + VERIFY( s.empty() ); + + s.push_range(Range(a, a+4)); + VERIFY( eq(s, {a, 4}) ); + + s.push_range(Range(a+4, a+9)); + VERIFY( eq(s, {a, 9}) ); +} + +template> +struct NoAppendRangeCont : std::deque +{ + template + void append_range(R&&) = delete; +}; + +template +void +do_test_c() +{ + do_test>(); + do_test>(); + do_test>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_c>(); + do_test_c>(); + do_test_c>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h b/libstdc++-v3/testsuite/util/testsuite_iterators.h index 2895ff8c1b9..0df6dcc5af5 100644 --- a/libstdc++-v3/testsuite/util/testsuite_iterators.h +++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h @@ -867,6 +867,17 @@ namespace __gnu_test typename Iter::ContainerType bounds; }; + // A move-only type meeting the minimum std::range requirements + template class Iter> + struct test_range_nocopy : test_range + { + test_range_nocopy(T* first, T* last) : test_range(first, last) + {} + + test_range_nocopy(test_range_nocopy&&) = default; + test_range_nocopy& operator=(test_range_nocopy&&) = default; + }; + template using test_contiguous_range = test_range;