[libcxx-commits] [PATCH] D116303: [libc++][ranges] Implement std::ranges::swap_ranges()
Arthur O'Dwyer via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jan 25 07:50:25 PST 2022
Quuxplusone added inline comments.
================
Comment at: libcxx/include/__algorithm/ranges_swap_ranges.h:32-35
+ template <input_iterator _I1, sentinel_for<_I1> _S1,
+ input_iterator _I2, sentinel_for<_I2> _S2>
+ requires indirectly_swappable<_I1, _I2> _LIBCPP_HIDE_FROM_ABI constexpr ranges::swap_ranges_result<_I1, _I2>
+ operator()(_I1 __first1, _S1 __last1, _I2 __first2, _S2 __last2) const {
----------------
Ditto below.
================
Comment at: libcxx/include/__algorithm/ranges_swap_ranges.h:41
+ }
+ return {__first1, __first2};
+ }
----------------
I believe you're missing `_VSTD::move` on both operands, which means there's some test coverage missing. Please investigate and add regression tests involving `cpp20_input_iterator`.
================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.swap/ranges.swap_ranges.pass.cpp:122-126
+ using Iter = cpp17_input_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iter>;
+ using Expected = std::ranges::swap_ranges_result<Iter, Iter>;
+ std::same_as<Expected> auto r =
+ std::ranges::swap_ranges(Iter(i), Sentinel(Iter(i + 3)), Iter(j), Sentinel(Iter(j + 3)));
----------------
Consider `s/Iter/It/g`, `s/Sentinel/Sent/g`, to shorten these lines.
================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.swap/ranges.swap_ranges.pass.cpp:154-162
+ using Expected1 = std::ranges::swap_ranges_result<int*, std::ranges::dangling>;
+ using Expected2 = std::ranges::swap_ranges_result<std::ranges::dangling, int*>;
+ std::array<int, 3> r = {1, 2, 3};
+ std::same_as<Expected1> auto a = std::ranges::swap_ranges(r, std::array{4, 5, 6});
+ assert((r == std::array{4, 5, 6}));
+ assert(a.in1 == r.begin() + 3);
+ std::same_as<Expected2> auto b = std::ranges::swap_ranges(std::array{1, 2, 3}, r);
----------------
Awesome constexprification! But please put each of the two tests in its own `{ }` scope as usual, so `Expected2` is defined closer to its point of use. (This requires re-defining `r` in each scope; I have no problem with that.)
================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.swap/ranges.swap_ranges.pass.cpp:84
+
+constexpr void test_borrowed_input_range() {
+ int r1[] = {1, 2, 3};
----------------
Quuxplusone wrote:
> IIUC, the special thing about borrowed input ranges is that their iterators don't dangle. So this test should be testing with `std::move(b1)`, not just `b1`. And //that// means that it should simply be merged into `test_rval_range()`, something like
> ```
> std::vector<int> x = {1, 2, 3};
> using It = std::vector<int>::iterator;
> std::same_as<std::ranges::swap_ranges_result<It, It>> auto c =
> std::ranges::swap_ranges(std::views::all(x), r);
> assert(c.in1 == x.end());
> assert(c.in2 == r.end());
> assert((r == std::vector<int>{1, 2, 3}));
> assert((x == std::vector<int>{4, 5, 6}));
> ```
> where `std::views::all(x)` is our rvalue, borrowed, range.
>
>
> Going further down this rabbit hole... Notice that both `subrange` and `all_t` (i.e. `ref_view` in this case) are both not only //borrowed// ranges, but also //views//. We don't have test coverage for a borrowed non-view. However, such things seem vanishingly rare? I think the trick is to make it immobile:
> ```
> struct BorrowedNonView : std::view_base {
> BorrowedNonView(BorrowedNonView&&) = delete;
> int *begin() const;
> int *end() const;
> };
> ```
> I would say that this is not worth it (especially since `swap_ranges` doesn't do anything surprising w.r.t. dangling).
On the `ranges::mismatch` review you pointed out that `views::all(x)` i.e. `ref_view` is not a borrowed range, and it looks like you're right w.r.t. libc++, but that seems to be a libc++ bug.
https://en.cppreference.com/w/cpp/ranges/borrowed_range
I'll fix it, and then `views::all(x)` should be acceptable as a borrowed range in these tests.
I think `std::ranges::subrange(x)` is also acceptable, but it's slightly longer and less idiomatic than `views::all`.
Actually perhaps we should make a new dedicated test type, in `support/test_range.h`:
```
template<class R>
struct BorrowedRange {
explicit BorrowedRange(const R& r) : r_(&r) {}
BorrowedRange(BorrowedRange&&) = delete; // immobile
auto begin() const { return std::ranges::begin(*r_); }
auto end() const { return std::ranges::end(*r_); }
const R *r_;
};
static_assert( std::ranges::borrowed_range<BorrowedRange<int[10]>>);
static_assert(!std::ranges::view<BorrowedRange<int[10]>>);
template<class R>
BorrowedRange<R> make_borrowed_range(const R& r) {
static_assert(std::ranges::range<const R>);
return BorrowedRange<R>(r);
}
```
Then line 87 becomes
```
std::ranges::swap_ranges(make_borrowed_range(r1), r2);
```
and it's more self-documenting.
However, the existing `support/test_range.h` [sic] looks a little messy, so maybe I'll tackle that at some point this week myself, unless you really //want// to tackle it.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D116303/new/
https://reviews.llvm.org/D116303
More information about the libcxx-commits
mailing list