| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // UNSUPPORTED: c++03, c++11, c++14, c++17 |
| |
| // template<bool OtherConst> |
| // requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> |
| // friend constexpr range_difference_t<maybe-const<OtherConst, V>> |
| // operator-(const iterator<OtherConst>& x, const sentinel& y); |
| // |
| // template<bool OtherConst> |
| // requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> |
| // friend constexpr range_difference_t<maybe-const<OtherConst, V>> |
| // operator-(const sentinel& x, const iterator<OtherConst>& y); |
| |
| #include <cassert> |
| #include <concepts> |
| #include <cstddef> |
| #include <functional> |
| #include <ranges> |
| #include <tuple> |
| |
| #include "../types.h" |
| |
| template <bool Const> |
| struct Iter { |
| std::tuple<int>* it_; |
| |
| using value_type = std::tuple<int>; |
| using difference_type = std::ptrdiff_t; |
| using iterator_concept = std::input_iterator_tag; |
| |
| constexpr decltype(auto) operator*() const { return *it_; } |
| constexpr Iter& operator++() { |
| ++it_; |
| return *this; |
| } |
| constexpr void operator++(int) { ++it_; } |
| }; |
| |
| template <bool Const> |
| struct Sent { |
| std::tuple<int>* end_; |
| |
| constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; } |
| }; |
| |
| template <bool Const> |
| struct SizedSent { |
| std::tuple<int>* end_; |
| |
| constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; } |
| |
| friend constexpr auto operator-(const SizedSent& st, const Iter<Const>& it) { return st.end_ - it.it_; } |
| |
| friend constexpr auto operator-(const Iter<Const>& it, const SizedSent& st) { return it.it_ - st.end_; } |
| }; |
| |
| template <bool Const> |
| struct CrossSizedSent { |
| std::tuple<int>* end_; |
| |
| template <bool C> |
| constexpr bool operator==(const Iter<C>& i) const { |
| return i.it_ == end_; |
| } |
| |
| template <bool C> |
| friend constexpr auto operator-(const CrossSizedSent& st, const Iter<C>& it) { |
| return st.end_ - it.it_; |
| } |
| |
| template <bool C> |
| friend constexpr auto operator-(const Iter<C>& it, const CrossSizedSent& st) { |
| return it.it_ - st.end_; |
| } |
| }; |
| |
| template <template <bool> class It, template <bool> class St> |
| struct Range : TupleBufferView { |
| using TupleBufferView::TupleBufferView; |
| |
| using iterator = It<false>; |
| using sentinel = St<false>; |
| using const_iterator = It<true>; |
| using const_sentinel = St<true>; |
| |
| constexpr iterator begin() { return {buffer_}; } |
| constexpr const_iterator begin() const { return {buffer_}; } |
| constexpr sentinel end() { return sentinel{buffer_ + size_}; } |
| constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; } |
| }; |
| |
| template <class T, class U> |
| concept HasMinus = requires(const T t, const U u) { t - u; }; |
| |
| template <class BaseRange> |
| using ElementsView = std::ranges::elements_view<BaseRange, 0>; |
| |
| template <class BaseRange> |
| using ElemIter = std::ranges::iterator_t<ElementsView<BaseRange>>; |
| |
| template <class BaseRange> |
| using EleConstIter = std::ranges::iterator_t<const ElementsView<BaseRange>>; |
| |
| template <class BaseRange> |
| using EleSent = std::ranges::sentinel_t<ElementsView<BaseRange>>; |
| |
| template <class BaseRange> |
| using EleConstSent = std::ranges::sentinel_t<const ElementsView<BaseRange>>; |
| |
| constexpr void testConstraints() { |
| // base is not sized |
| { |
| using Base = Range<Iter, Sent>; |
| static_assert(!HasMinus<EleSent<Base>, ElemIter<Base>>); |
| static_assert(!HasMinus<ElemIter<Base>, EleSent<Base>>); |
| |
| static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>); |
| static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>); |
| |
| static_assert(!HasMinus<EleConstSent<Base>, EleConstIter<Base>>); |
| static_assert(!HasMinus<EleConstIter<Base>, EleConstSent<Base>>); |
| |
| static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>); |
| static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>); |
| } |
| |
| // base is sized but not cross const |
| { |
| using Base = Range<Iter, SizedSent>; |
| static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>); |
| static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>); |
| |
| static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>); |
| static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>); |
| |
| static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>); |
| static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>); |
| |
| static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>); |
| static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>); |
| } |
| |
| // base is cross const sized |
| { |
| using Base = Range<Iter, CrossSizedSent>; |
| static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>); |
| static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>); |
| |
| static_assert(HasMinus<EleSent<Base>, EleConstIter<Base>>); |
| static_assert(HasMinus<EleConstIter<Base>, EleSent<Base>>); |
| |
| static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>); |
| static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>); |
| |
| static_assert(HasMinus<EleConstSent<Base>, ElemIter<Base>>); |
| static_assert(HasMinus<ElemIter<Base>, EleConstSent<Base>>); |
| } |
| } |
| |
| constexpr bool test() { |
| std::tuple<int> buffer[] = {{1}, {2}, {3}, {4}, {5}}; |
| |
| // base is sized but not cross const |
| { |
| using Base = Range<Iter, SizedSent>; |
| Base base{buffer}; |
| auto ev = base | std::views::elements<0>; |
| auto iter = ev.begin(); |
| auto const_iter = std::as_const(ev).begin(); |
| auto sent = ev.end(); |
| auto const_sent = std::as_const(ev).end(); |
| |
| assert(iter - sent == -5); |
| assert(sent - iter == 5); |
| assert(const_iter - const_sent == -5); |
| assert(const_sent - const_iter == 5); |
| } |
| |
| // base is cross const sized |
| { |
| using Base = Range<Iter, CrossSizedSent>; |
| Base base{buffer}; |
| auto ev = base | std::views::elements<0>; |
| auto iter = ev.begin(); |
| auto const_iter = std::as_const(ev).begin(); |
| auto sent = ev.end(); |
| auto const_sent = std::as_const(ev).end(); |
| |
| assert(iter - sent == -5); |
| assert(sent - iter == 5); |
| assert(iter - const_sent == -5); |
| assert(const_sent - iter == 5); |
| assert(const_iter - sent == -5); |
| assert(sent - const_iter == 5); |
| assert(const_iter - const_sent == -5); |
| assert(const_sent - const_iter == 5); |
| } |
| |
| return true; |
| } |
| |
| int main(int, char**) { |
| test(); |
| static_assert(test()); |
| |
| return 0; |
| } |