diff --git a/include/preview/__ranges/views/join_view.h b/include/preview/__ranges/views/join_view.h index b92ffde..a78b143 100644 --- a/include/preview/__ranges/views/join_view.h +++ b/include/preview/__ranges/views/join_view.h @@ -19,12 +19,14 @@ #include "preview/__iterator/iter_move.h" #include "preview/__iterator/iter_swap.h" #include "preview/__memory/addressof.h" +#include "preview/optional.h" #include "preview/__ranges/bidirectional_range.h" #include "preview/__ranges/common_range.h" #include "preview/__ranges/simple_view.h" #include "preview/__ranges/forward_range.h" #include "preview/__ranges/input_range.h" #include "preview/__ranges/movable_box.h" +#include "preview/__ranges/non_propagating_cache.h" #include "preview/__ranges/range_difference_t.h" #include "preview/__ranges/range_reference_t.h" #include "preview/__ranges/sentinel_t.h" @@ -43,6 +45,11 @@ namespace preview { namespace ranges { namespace detail { +template +constexpr T& as_lvalue(T&& x) noexcept { + return static_cast(x); +} + template using join_view_iterator_concept = conditional_t< @@ -92,26 +99,97 @@ struct join_view_iterator_category { >; }; -} // namespace detail +template::value /* true */> +struct join_view_iterator_outer_iter { + join_view_iterator_outer_iter() = default; -template -class join_view : public view_interface> { + explicit join_view_iterator_outer_iter(OuterIter outer) + : outer_(std::move(outer)) {} + + template + constexpr OuterIter& outer(Parent&) { + return outer_; + } + + template + constexpr const OuterIter& outer(Parent&) const { + return outer_; + } + + OuterIter outer_ = OuterIter(); +}; + +template +struct join_view_iterator_outer_iter { + join_view_iterator_outer_iter() = default; + + explicit join_view_iterator_outer_iter(OuterIter) {} + + template + constexpr OuterIter& outer(Parent& p) { + return *p->outer_; + } + + template + constexpr const OuterIter& outer(Parent& p) const { + return *p->outer_; + } +}; + +template>::value /* true */> +class join_view_outer_base : public view_interface { public: - static_assert(input_range::value, "Constraints not satisfied"); - static_assert(view::value, "Constraints not satisfied"); - static_assert(input_range>::value, "Constraints not satisfied"); + + + protected: + non_propagating_cache> outer_; +}; + +template +class join_view_outer_base : public view_interface { + public: + +}; + +template>>::value /* true */> +class join_view_inner_base : public join_view_outer_base { + public: + + protected: + non_propagating_cache>> inner_; +}; + +template +class join_view_inner_base : public join_view_outer_base { + public: +}; + +} // namespace detail + +template +class join_view : public detail::join_view_inner_base, V> { private: template using InnerRng = range_reference_t>; public: + static_assert(input_range::value, "Constraints not satisfied"); + static_assert(view::value, "Constraints not satisfied"); + static_assert(input_range>::value, "Constraints not satisfied"); + template class iterator; template friend class iterator; template class sentinel; template - class iterator : detail::join_view_iterator_category> { + class iterator + : public detail::join_view_iterator_category> + , private detail::join_view_iterator_outer_iter< + maybe_const, // Base + iterator_t> // OuterIter + > + { using Parent = maybe_const; using Base = maybe_const; using OuterIter = iterator_t; @@ -121,6 +199,71 @@ class join_view : public view_interface> { friend class join_view; template friend class sentinel; + using outer_iter_base = detail::join_view_iterator_outer_iter< + maybe_const, + iterator_t> + >; + + constexpr OuterIter& outer() { + return outer_iter_base::outer(parent_); + } + + constexpr const OuterIter& outer() const { + return outer_iter_base::outer(parent_); + } + + constexpr decltype(auto) update_inner_impl(const iterator_t& x, std::true_type /* ref_is_glvalue */) { + return *x; + } + constexpr decltype(auto) update_inner_impl(const iterator_t& x, std::false_type /* ref_is_glvalue */) { + return parent_->inner_.emplace_deref(x); + } + + constexpr decltype(auto) update_inner(const iterator_t& x) { + return update_inner_impl(x, bool_constant{}); + } + + constexpr void satisfy_impl(std::true_type /* ref_is_glvalue */) { + for (; outer() != ranges::end(parent_->base_); ++outer()) { + auto&& inner = update_inner(outer()); + inner_ = ranges::begin(inner); + if (*inner_ != ranges::end(inner)) + return; + } + inner_.reset(); + } + constexpr void satisfy_impl(std::false_type /* ref_is_glvalue */) { + for (; outer() != ranges::end(parent_->base_); ++outer()) { + auto&& inner = update_inner(outer());; + inner_ = ranges::begin(inner); + if (inner_ != ranges::end(inner)) + return; + } + } + + constexpr void satisfy() { + satisfy_impl(bool_constant{}); + } + + template, + forward_range + >::value, int> = 0> + constexpr iterator(Parent& parent, OuterIter outer) + : outer_iter_base(std::move(outer)) + , parent_(preview::addressof(parent)) + { + satisfy(); + } + + template, + negation> + >::value, int> = 0> + constexpr explicit iterator(Parent& parent) + : parent_(preview::addressof(parent)) + { + satisfy(); + } + public: using iterator_concept = detail::join_view_iterator_concept; @@ -131,18 +274,7 @@ class join_view : public view_interface> { using reference = decltype(**std::declval&>()); #endif - template, - default_initializable - >::value, int> = 0> - iterator() - : outer_(), inner_(), parent_(nullptr) {} - - constexpr iterator(Parent& parent, OuterIter outer) - : outer_(std::move(outer)), parent_(preview::addressof(parent)) - { - satisfy(); - } + iterator() = default; template, @@ -150,51 +282,56 @@ class join_view : public view_interface> { convertible_to>, InnerIter> >::value, int> = 0> constexpr iterator(iterator i) - : outer_(std::move(i.outer_)), inner_(std::move(i.inner_)), parent_(i.parent_) {} + : outer_iter_base(std::move(i.outer())) + , inner_(std::move(i.inner_)) + , parent_(i.parent_) {} constexpr decltype(auto) operator*() const { return **inner_; } - template, - copyable + template, + has_operator_arrow, + copyable >::value, int> = 0> constexpr decltype(auto) operator->() const { return *inner_; } - template = 0> constexpr iterator& operator++() { - auto&& inner_rng = *outer_; - if (++*inner_ == ranges::end(inner_rng)) { - ++outer_; + return pre_increment(bool_constant{}); + } + + private: + constexpr iterator& pre_increment(std::true_type) { + if (++*inner_ == ranges::end(detail::as_lvalue(*outer()))) { + ++outer(); satisfy(); } return *this; } - - template = 0> - constexpr iterator& operator++() { - auto&& inner_rng = *parent_->inner_; - if (++*inner_ == ranges::end(inner_rng)) { - ++outer_; + constexpr iterator& pre_increment(std::false_type) { + if (++*inner_ == ranges::end(detail::as_lvalue(*parent_->inner_))) { + ++outer(); satisfy(); } return *this; } - template, - forward_range, - forward_range> - >::value == false, int> = 0> + public: + template, + negation, + forward_range, + forward_range> + >> + >::value, int> = 0> constexpr void operator++(int) { ++*this; } - template, + template, + bool_constant, forward_range, forward_range> >::value, int> = 0> @@ -204,23 +341,23 @@ class join_view : public view_interface> { return tmp; } - template, + template, + bool_constant, bidirectional_range, bidirectional_range>, common_range> >::value, int> = 0> constexpr iterator& operator--() { - if (outer_ == ranges::end(parent_->base_)) - inner_ = ranges::end(*--outer_); - while (*inner_ == ranges::begin(*outer_)) - inner_ = ranges::end(*--outer_); + if (outer() == ranges::end(parent_->base_)) + inner_ = ranges::end(detail::as_lvalue(*--outer())); + while (*inner_ == ranges::begin(detail::as_lvalue(*outer()))) + inner_ = ranges::end(detail::as_lvalue(*--outer())); --*inner_; return *this; } - template, + template, + bool_constant, bidirectional_range, bidirectional_range>, common_range> @@ -231,22 +368,18 @@ class join_view : public view_interface> { return tmp; } - constexpr void satisfy() { - satisfy_impl(bool_constant{}); - } - - template, - equality_comparable>, + template, + bool_constant, + forward_range, equality_comparable>> >::value, int> = 0> friend constexpr bool operator==(const iterator& x, const iterator& y) { - return (x.outer_ == y.outer_) && (*x.inner_ == *y.inner_); + return (x.outer_ == y.outer_) && (x.inner_ == y.inner_); } - template, - equality_comparable>, + template, + bool_constant, + forward_range, equality_comparable>> >::value, int> = 0> friend constexpr bool operator!=(const iterator& x, const iterator& y) { @@ -259,66 +392,19 @@ class join_view : public view_interface> { return ranges::iter_move(*i.inner_); } - template::value, int> = 0> + template, + indirectly_swappable + >::value, int> = 0> friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_))) { - ranges::iter_swap(x.inner_, y.inner_); + ranges::iter_swap(*x.inner_, *y.inner_); } private: - constexpr OuterIter& get_outer() noexcept { - return get_outer(forward_range{}); - } - constexpr const OuterIter& get_outer() const noexcept { - return get_outer(forward_range{}); - } - constexpr OuterIter& get_outer_impl(std::true_type /* forward_range */) noexcept { - return outer_; - } - constexpr OuterIter& get_outer_impl(std::false_type /* forward_range */) noexcept { - return *parent_->outer_; - } - constexpr OuterIter& get_outer_impl(std::true_type /* forward_range */) const noexcept { - return outer_; - } - constexpr OuterIter& get_outer_impl(std::false_type /* forward_range */) const noexcept { - return *parent_->outer_; - } - - constexpr decltype(auto) update_inner_impl(const iterator_t& x, std::true_type /* ref_is_glvalue */) { - return *x; - } - constexpr decltype(auto) update_inner_impl(const iterator_t& x, std::false_type /* ref_is_glvalue */) { - return parent_->inner_.emplace(x); - } - - constexpr decltype(auto) update_inner(const iterator_t& x) { - return update_inner_impl(x, bool_constant{}); - } - constexpr void satisfy_impl(std::true_type /* ref_is_glvalue */) { - for (; outer_ != ranges::end(parent_->base_); ++outer_) { - auto&& inner = update_inner(outer_); - inner_ = ranges::begin(inner); - if (*inner_ != ranges::end(inner)) - return; - } - - inner_ = InnerIter(); - } - constexpr void satisfy_impl(std::false_type /* ref_is_glvalue */) { - for (; outer_ != ranges::end(parent_->base_); ++outer_) { - auto&& inner = update_inner(outer_);; - inner_ = ranges::begin(inner); - if (inner_ != ranges::end(inner)) - return; - } - } - - OuterIter outer_; - movable_box inner_; - Parent* parent_; + optional inner_; + Parent* parent_ = nullptr; }; template @@ -339,33 +425,46 @@ class join_view : public view_interface> { constexpr sentinel(sentinel s) : end_(std::move(s.end_)) {} - friend constexpr bool operator==(const iterator& x, const sentinel& y) { - using namespace preview::rel_ops; - return x.get_outer() == y.end_; + template, iterator_t>> + ::value, int> = 0> + friend constexpr bool operator==(const iterator& x, const sentinel& y) { + return x.outer() == y.end_; } - friend constexpr bool operator==(const sentinel& y, const iterator& x) { - return x == y; + template, iterator_t>> + ::value, int> = 0> + friend constexpr bool operator!=(const iterator& x, const sentinel& y) { + return !(x == y); } - friend constexpr bool operator!=(const iterator& x, const sentinel& y) { - return !(x == y); + template, iterator_t>> + ::value, int> = 0> + friend constexpr bool operator==(const sentinel& y, const iterator& x) { + return x == y; } - friend constexpr bool operator!=(const sentinel& y, const iterator& x) { + template, iterator_t>> + ::value, int> = 0> + friend constexpr bool operator!=(const sentinel& y, const iterator& x) { return !(x == y); } private: - sentinel_t end_; + sentinel_t end_{}; }; - template::value, int> = 0> - join_view() : base_(V()) {} + join_view() = default; - constexpr explicit join_view(V base) : base_(std::move(base)) {} + constexpr explicit join_view(V base) + : base_(std::move(base)) {} - template::value, int> = 0> + template, + copy_constructible + >::value, int> = 0> constexpr V base() const& { return base_; } @@ -374,59 +473,76 @@ class join_view : public view_interface> { return std::move(base_); } + // vvvvvvvvvv begin vvvvvvvvvv constexpr auto begin() { - using B = conjunction, std::is_reference>>; + return begin_impl(forward_range{}); + } + + private: + constexpr auto begin_impl(std::true_type /* forward_range */) { + using B = conjunction, std::is_reference>>; return iterator{*this, ranges::begin(base_)}; } + constexpr auto begin_impl(std::false_type /* forward_range */) { + this->outer_ = ranges::begin(base_); + return iterator{*this}; + } + // ^^^^^^^^^^ begin ^^^^^^^^^^ - template, - std::is_reference> + public: + template, + forward_range, + std::is_reference>, + input_range> >::value, int> = 0> constexpr auto begin() const { return iterator{*this, ranges::begin(base_)}; } + // vvvvvvvvvv end() vvvvvvvvvv + constexpr auto end() { return end_impl(conjunction< forward_range, - std::is_reference>, - forward_range>, - common_range, - common_range> - >{}); + std::is_reference>, forward_range>, + common_range, common_range> + >{}); + } + + private: + constexpr auto end_impl(std::true_type) { + return iterator::value>{*this, ranges::end(base_)}; + } + constexpr auto end_impl(std::false_type) { + return sentinel::value>{*this}; } + // ^^^^^^^^^^ end() ^^^^^^^^^^ - template, - std::is_reference> + // vvvvvvvvvv end() const vvvvvvvvvv + public: + template, + forward_range, + std::is_reference>, + input_range> >::value, int> = 0> constexpr auto end() const { return end_impl(conjunction< - forward_range, - std::is_reference>, forward_range>, common_range, common_range> - >{}); + >{}); } private: - constexpr auto end_impl(std::true_type) { - return iterator::value>{*this, ranges::end(base_)}; - } - constexpr auto end_impl(std::false_type) { - return sentinel::value>{*this}; - } - constexpr auto end_impl(std::true_type) const { return iterator{*this, ranges::end(base_)}; } constexpr auto end_impl(std::false_type) const { return sentinel{*this}; } + // ^^^^^^^^^^ end() const ^^^^^^^^^^ - V base_; + V base_{}; }; #if __cplusplus >= 201703L diff --git a/test/ranges_views.cc b/test/ranges_views.cc index 572af0c..7e725cf 100644 --- a/test/ranges_views.cc +++ b/test/ranges_views.cc @@ -723,3 +723,40 @@ TEST(VERSIONED(RangesViews), drop_while_view) { {3, 4, 5} )); } + +TEST(VERSIONED(RangesViews), join_view) { + using namespace std::literals; + using namespace preview::literals; + + const auto bits = {"https:"sv, "//"sv, "cppreference"sv, "."sv, "com"sv}; + for (char const c : bits | views::join) + std::cout << c; + std::cout << '\n'; + EXPECT_TRUE(ranges::equal(bits | views::join, "https://cppreference.com"_sv)); + + const std::vector> v{{1, 2}, {3, 4, 5}, {6}, {7, 8, 9}}; +#if PREVIEW_CXX_VERSION >= 17 + auto jv = ranges::join_view(v); +#else + auto jv = views::join(v); +#endif + for (int const e : jv) + std::cout << e << ' '; + std::cout << '\n'; + EXPECT_TRUE(ranges::equal(jv, views::iota(1, 10))); + + std::vector ss{"hello", " ", "world", "!"}; + for (char ch : ss | views::join) + std::cout << ch; + EXPECT_TRUE(ranges::equal( + ss | views::join, + "hello world!"s + )); +} + +struct Person { int age; std::string name; }; +TEST(VERSIONED(RangesViews), join_view_P2328R1) { + std::vector v; + (void)(v | views::transform([](auto& p) -> std::string& { return p.name; }) + | views::join); // OK +}