[libcxx-commits] [PATCH] D107671: [libcxx][ranges] Add `ranges::join_view`.
Louis Dionne via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Aug 11 12:52:21 PDT 2021
ldionne requested changes to this revision.
ldionne added inline comments.
This revision now requires changes to proceed.
================
Comment at: libcxx/include/__ranges/join_view.h:30
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
----------------
Not needed.
================
Comment at: libcxx/include/__ranges/join_view.h:36-42
+#define _CONSTEXPR_TERNARY(cond, a, b) \
+ [&]() -> auto&& { \
+ if constexpr (cond) \
+ return a; \
+ else \
+ return b; \
+ }();
----------------
If you agree with what I say below, this should be removed.
================
Comment at: libcxx/include/__ranges/join_view.h:80
+ : public view_interface<join_view<_View>>
+ , public __inner_cache<range_reference_t<_View>> {
+ private:
----------------
Instead, I would do:
```
static constexpr bool _UseCache = !is_reference_v<_InnerRange>;
using _Cache = _If<_UseCache, __non_propagating_cache<remove_cv_t<_InnerRange>>, __empty_cache>;
[[no_unique_address]] _Cache __cache_;
```
That way, you get rid of `__inner_cache` altogether.
================
Comment at: libcxx/include/__ranges/join_view.h:84
+
+ template<bool _Const> struct __iterator
+ : public __join_view_iterator_category<__maybe_const<_Const, _View>> {
----------------
`__iterator` and `__sentinel` are templates -- can we define them out-of-line?
================
Comment at: libcxx/include/__ranges/join_view.h:101
+ private:
+ optional<_Inner> __inner_; // TODO: lwg issue?
+ _Parent *__parent_ = nullptr;
----------------
I think you should remove the TODO since this is https://wg21.link/LWG3569 and it will almost certainly be voted into the Standard.
================
Comment at: libcxx/include/__ranges/join_view.h:106-116
+ for (; __outer_ != ranges::end(__parent_->__base_); ++__outer_) {
+ auto&& __inner = _CONSTEXPR_TERNARY(__ref_is_glvalue,
+ *__outer_,
+ __parent_->__inner_.__emplace_deref(__outer_));
+ __inner_ = ranges::begin(__inner);
+ if (*__inner_ != ranges::end(__inner))
+ return;
----------------
IMO the spec is easier to understand as-is, and it doesn't require introducing a `_CONSTEXPR_TERNARY` macro that is only used in two places.
I don't mind if you use a IIFE or not.
================
Comment at: libcxx/include/__ranges/join_view.h:109
+ *__outer_,
+ __parent_->__inner_.__emplace_deref(__outer_));
+ __inner_ = ranges::begin(__inner);
----------------
I don't understand why the spec allows modifying a member of the parent `join_view` from the iterator. That's very sneaky and can lead to e.g. race conditions if someone does:
```
join_view v = ...;
auto it1 = v.begin();
auto it2 = v.begin();
std::thread([=]{
++it1; // calls satisfy() under the hood, which modifies `v`
});
std::thread([=]{
++it2; // calls satisfy() under the hood, which modifies `v`
});
// sneaky race condition since you had a copy of different iterators in each thread
```
Basically, I think it's quite vexing that incrementing an iterator modifies the underlying view in this case, and I don't fully understand why that is even required. What use case does that enable? Is the criteria `is_reference_v<range_reference_t<V>>` meant to mean something like "the outer range creates its inner ranges on-the-fly and they are temporary objects"? I'm having trouble following here.
CC @tcanens in case you can share some insight here.
================
Comment at: libcxx/include/__ranges/join_view.h:136-137
+ _LIBCPP_HIDE_FROM_ABI
+ __iterator() requires default_initializable<_Outer> &&
+ default_initializable<_Inner> = default;
+
----------------
You don't need the constraint anymore, since you're storing an optional and never default-constructing your inner iterator.
That isn't what the proposed resolution of https://wg21.link/LWG3569 says right now, but I'll send an email to LWG to request a change to the proposed resolution, since I think it's likely just an oversight.
Can you please add tests where the inner iterator is not default constructible to make sure that we can default construct the `join_view::__iterator`? Basically what we'd add if we were fixing the LWG issue after having implemented `join_view` per the current spec.
================
Comment at: libcxx/include/__ranges/join_view.h:181
+ constexpr void operator++(int) {
+ return ++*this;
+ }
----------------
I think this should not be `return ++*this`, but merely `++*this`. Otherwise, if `++*this` returns something else than `void`, you'll get an error like `void function '<name>' should not return a value`.
Please add a test that exercises that.
================
Comment at: libcxx/include/__ranges/join_view.h:205-207
+ while (*__inner_ == ranges::begin(*__outer_)) {
+ __inner_ = ranges::end(*--__outer_);
+ }
----------------
================
Comment at: libcxx/include/__ranges/join_view.h:250
+
+ template<bool _Const> struct __sentinel {
+ template<bool> friend struct __sentinel;
----------------
Please define out-of-line.
================
Comment at: libcxx/include/__ranges/join_view.h:286
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr explicit join_view(_View __base)
+ : __base_(_VSTD::move(__base)) {}
----------------
Make sure you test `explicit`-ness.
================
Comment at: libcxx/include/__ranges/join_view.h:313-315
+ if constexpr (forward_range<_View> &&
+ is_reference_v<_InnerRange> && forward_range<_InnerRange> &&
+ common_range<_View> && common_range<_InnerRange>)
----------------
This simple re-formatting makes the similarity between this and the `const` version below more obvious.
================
Comment at: libcxx/include/__ranges/join_view.h:327-331
+ if constexpr (forward_range<const _View> &&
+ is_reference_v<range_reference_t<const _View>> &&
+ forward_range<range_reference_t<const _View>> &&
+ common_range<const _View> &&
+ common_range<range_reference_t<const _View>>) {
----------------
Same reason as above, this makes the similarity between the `const` and the non-`const` versions more obvious.
================
Comment at: libcxx/include/__ranges/join_view.h:340
+ template<class _Range>
+ explicit join_view(_Range&&) -> join_view<views::all_t<_Range>>;
+
----------------
Please make sure you test the explicit-ness of this deduction guide with something like that:
```
join_view v = some_range; // should not compile
join_view v(some_range); // should compile
```
================
Comment at: libcxx/include/__ranges/non_propagating_cache.h:89-95
+ template<class _Other>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr _Tp& __emplace_deref(const _Other& __value) {
+ __value_.reset();
+ __value_.emplace(*__value);
+ return *__value_;
+ }
----------------
tcanens wrote:
> This isn't right. See https://github.com/microsoft/STL/pull/2038#discussion_r683086306
I'll make this change in a separate patch, I think I have something that works in a WIP branch right now. Thanks for bringing this to our attention.
================
Comment at: libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp:1
+//===----------------------------------------------------------------------===//
+//
----------------
This is a general comment not only applicable to this test. We seem to be missing coverage for a lot of range "topologies" (what I mean by that is an empty range, a range with 1 subrange, 2 subranges, an empty subrange, subranges of empty sizes, etc..).
I don't think it is reasonable to replicate each test for all topologies, but we should add tests with something like a random access range (const and non-const) checking all topologies. What I mean here is that I don't care if you don't test a range like `[[x], [], [y, z]]` with all combinations of archetypes, but you should test it at least with a few that are relevant (that probably is restricted to const and non-const).
================
Comment at: libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp:11
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
----------------
Not needed (here and everywhere else).
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D107671/new/
https://reviews.llvm.org/D107671
More information about the libcxx-commits
mailing list