[libcxx][ranges] Add `ranges::join_view`.

Differential Revision: https://reviews.llvm.org/D107671
This commit is contained in:
zoecarver 2021-08-06 15:33:46 -07:00
parent 7b20e05c71
commit df324bba5c
33 changed files with 1898 additions and 1 deletions

View File

@ -140,7 +140,7 @@ Section,Description,Dependencies,Assignee,Complete
`[range.transform] <http://wg21.link/range.transform>`_,`transform_view <https://llvm.org/D103056>`_,[range.all],Zoe Carver,✅
`[range.iota] <http://wg21.link/range.iota>`_,iota_view,[range.all],Zoe Carver,✅
`[range.take] <http://wg21.link/range.take>`_,take_view,[range.all],Zoe Carver,✅
`[range.join] <http://wg21.link/range.join>`_,join_view,[range.all],Zoe Carver,In Progress
`[range.join] <http://wg21.link/range.join>`_,join_view,[range.all],Zoe Carver,
`[range.empty] <http://wg21.link/range.empty>`_,`empty_view <https://llvm.org/D103208>`_,[view.interface],Zoe Carver,✅
`[range.single] <http://wg21.link/range.single>`_,single_view,[view.interface],Zoe Carver,✅
`[range.split] <http://wg21.link/range.split>`_,split_view,[range.all],Zoe Carver,In Progress

1 Section Description Dependencies Assignee Complete
140
141
142
143
144
145
146

View File

@ -224,6 +224,7 @@ set(files
__ranges/enable_borrowed_range.h
__ranges/enable_view.h
__ranges/iota_view.h
__ranges/join_view.h
__ranges/non_propagating_cache.h
__ranges/ref_view.h
__ranges/reverse_view.h

View File

@ -0,0 +1,350 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___RANGES_JOIN_VIEW_H
#define _LIBCPP___RANGES_JOIN_VIEW_H
#include <__config>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
#include <__ranges/non_propagating_cache.h>
#include <__ranges/ref_view.h>
#include <__ranges/subrange.h>
#include <__ranges/view_interface.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <optional>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#if !defined(_LIBCPP_HAS_NO_RANGES)
namespace ranges {
template<class>
struct __join_view_iterator_category {};
template<class _View>
requires is_reference_v<range_reference_t<_View>> &&
forward_range<_View> &&
forward_range<range_reference_t<_View>>
struct __join_view_iterator_category<_View> {
using _OuterC = typename iterator_traits<iterator_t<_View>>::iterator_category;
using _InnerC = typename iterator_traits<iterator_t<range_reference_t<_View>>>::iterator_category;
using iterator_category = _If<
derived_from<_OuterC, bidirectional_iterator_tag> && derived_from<_InnerC, bidirectional_iterator_tag>,
bidirectional_iterator_tag,
_If<
derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag>,
forward_iterator_tag,
input_iterator_tag
>
>;
};
template<input_range _View>
requires view<_View> && input_range<range_reference_t<_View>>
class join_view
: public view_interface<join_view<_View>> {
private:
using _InnerRange = range_reference_t<_View>;
template<bool> struct __iterator;
template<bool> struct __sentinel;
static constexpr bool _UseCache = !is_reference_v<_InnerRange>;
using _Cache = _If<_UseCache, __non_propagating_cache<remove_cvref_t<_InnerRange>>, __empty_cache>;
[[no_unique_address]] _Cache __cache_;
_View __base_ = _View(); // TODO: [[no_unique_address]] makes clang crash! File a bug :)
public:
_LIBCPP_HIDE_FROM_ABI
join_view() requires default_initializable<_View> = default;
_LIBCPP_HIDE_FROM_ABI
constexpr explicit join_view(_View __base)
: __base_(_VSTD::move(__base)) {}
_LIBCPP_HIDE_FROM_ABI
constexpr _View base() const& requires copy_constructible<_View> { return __base_; }
_LIBCPP_HIDE_FROM_ABI
constexpr _View base() && { return _VSTD::move(__base_); }
_LIBCPP_HIDE_FROM_ABI
constexpr auto begin() {
constexpr bool __use_const = __simple_view<_View> &&
is_reference_v<range_reference_t<_View>>;
return __iterator<__use_const>{*this, ranges::begin(__base_)};
}
template<class _V2 = _View>
_LIBCPP_HIDE_FROM_ABI
constexpr auto begin() const
requires input_range<const _V2> &&
is_reference_v<range_reference_t<const _V2>>
{
return __iterator<true>{*this, ranges::begin(__base_)};
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto end() {
if constexpr (forward_range<_View> &&
is_reference_v<_InnerRange> &&
forward_range<_InnerRange> &&
common_range<_View> &&
common_range<_InnerRange>)
return __iterator<__simple_view<_View>>{*this, ranges::end(__base_)};
else
return __sentinel<__simple_view<_View>>{*this};
}
template<class _V2 = _View>
_LIBCPP_HIDE_FROM_ABI
constexpr auto end() const
requires input_range<const _V2> &&
is_reference_v<range_reference_t<const _V2>>
{
using _ConstInnerRange = range_reference_t<const _View>;
if constexpr (forward_range<const _View> &&
is_reference_v<_ConstInnerRange> &&
forward_range<_ConstInnerRange> &&
common_range<const _View> &&
common_range<_ConstInnerRange>) {
return __iterator<true>{*this, ranges::end(__base_)};
} else {
return __sentinel<true>{*this};
}
}
};
template<input_range _View>
requires view<_View> && input_range<range_reference_t<_View>>
template<bool _Const> struct join_view<_View>::__sentinel {
template<bool> friend struct __sentinel;
private:
using _Parent = __maybe_const<_Const, join_view>;
using _Base = __maybe_const<_Const, _View>;
sentinel_t<_Base> __end_ = sentinel_t<_Base>();
public:
_LIBCPP_HIDE_FROM_ABI
__sentinel() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr explicit __sentinel(_Parent& __parent)
: __end_(ranges::end(__parent.__base_)) {}
_LIBCPP_HIDE_FROM_ABI
constexpr __sentinel(__sentinel<!_Const> __s)
requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
: __end_(_VSTD::move(__s.__end_)) {}
template<bool _OtherConst>
requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
return __x.__outer_ == __y.__end_;
}
};
template<input_range _View>
requires view<_View> && input_range<range_reference_t<_View>>
template<bool _Const> struct join_view<_View>::__iterator
: public __join_view_iterator_category<__maybe_const<_Const, _View>> {
template<bool> friend struct __iterator;
private:
using _Parent = __maybe_const<_Const, join_view>;
using _Base = __maybe_const<_Const, _View>;
using _Outer = iterator_t<_Base>;
using _Inner = iterator_t<range_reference_t<_Base>>;
static constexpr bool __ref_is_glvalue = is_reference_v<range_reference_t<_Base>>;
public:
_Outer __outer_ = _Outer();
private:
optional<_Inner> __inner_;
_Parent *__parent_ = nullptr;
_LIBCPP_HIDE_FROM_ABI
constexpr void __satisfy() {
for (; __outer_ != ranges::end(__parent_->__base_); ++__outer_) {
auto&& __inner = [&]() -> auto&& {
if constexpr (__ref_is_glvalue)
return *__outer_;
else
return __parent_->__cache_.__emplace_deref(__outer_);
}();
__inner_ = ranges::begin(__inner);
if (*__inner_ != ranges::end(__inner))
return;
}
if constexpr (__ref_is_glvalue)
__inner_.reset();
}
public:
using iterator_concept = _If<
__ref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<range_reference_t<_Base>>,
bidirectional_iterator_tag,
_If<
__ref_is_glvalue && forward_range<_Base> && forward_range<range_reference_t<_Base>>,
forward_iterator_tag,
input_iterator_tag
>
>;
using value_type = range_value_t<range_reference_t<_Base>>;
using difference_type = common_type_t<
range_difference_t<_Base>, range_difference_t<range_reference_t<_Base>>>;
_LIBCPP_HIDE_FROM_ABI
__iterator() requires default_initializable<_Outer> = default;
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator(_Parent& __parent, _Outer __outer)
: __outer_(_VSTD::move(__outer))
, __parent_(_VSTD::addressof(__parent)) {
__satisfy();
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator(__iterator<!_Const> __i)
requires _Const &&
convertible_to<iterator_t<_View>, _Outer> &&
convertible_to<iterator_t<_InnerRange>, _Inner>
: __outer_(_VSTD::move(__i.__outer_))
, __inner_(_VSTD::move(__i.__inner_))
, __parent_(__i.__parent_) {}
_LIBCPP_HIDE_FROM_ABI
constexpr decltype(auto) operator*() const {
return **__inner_;
}
_LIBCPP_HIDE_FROM_ABI
constexpr _Inner operator->() const
requires __has_arrow<_Inner> && copyable<_Inner>
{
return *__inner_;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator++() {
auto&& __inner = [&]() -> auto&& {
if constexpr (__ref_is_glvalue)
return *__outer_;
else
return *__parent_->__cache_;
}();
if (++*__inner_ == ranges::end(__inner)) {
++__outer_;
__satisfy();
}
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr void operator++(int) {
++*this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator operator++(int)
requires __ref_is_glvalue &&
forward_range<_Base> &&
forward_range<range_reference_t<_Base>>
{
auto __tmp = *this;
++*this;
return __tmp;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator--()
requires __ref_is_glvalue &&
bidirectional_range<_Base> &&
bidirectional_range<range_reference_t<_Base>> &&
common_range<range_reference_t<_Base>>
{
if (__outer_ == ranges::end(__parent_->__base_))
__inner_ = ranges::end(*--__outer_);
// Skip empty inner ranges when going backwards.
while (*__inner_ == ranges::begin(*__outer_)) {
__inner_ = ranges::end(*--__outer_);
}
--*__inner_;
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator operator--(int)
requires __ref_is_glvalue &&
bidirectional_range<_Base> &&
bidirectional_range<range_reference_t<_Base>> &&
common_range<range_reference_t<_Base>>
{
auto __tmp = *this;
--*this;
return __tmp;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
requires __ref_is_glvalue &&
equality_comparable<iterator_t<_Base>> &&
equality_comparable<iterator_t<range_reference_t<_Base>>>
{
return __x.__outer_ == __y.__outer_ && __x.__inner_ == __y.__inner_;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr decltype(auto) iter_move(const __iterator& __i)
noexcept(noexcept(ranges::iter_move(*__i.__inner_)))
{
return ranges::iter_move(*__i.__inner_);
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y)
noexcept(noexcept(ranges::iter_swap(*__x.__inner_, *__y.__inner_)))
requires indirectly_swappable<_Inner>
{
return ranges::iter_swap(*__x.__inner_, *__y.__inner_);
}
};
template<class _Range>
explicit join_view(_Range&&) -> join_view<views::all_t<_Range>>;
} // namespace ranges
#undef _CONSTEXPR_TERNARY
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___RANGES_JOIN_VIEW_H

View File

@ -85,6 +85,14 @@ namespace ranges {
constexpr void __set(_Tp const& __value) { __value_.emplace(__value); }
_LIBCPP_HIDE_FROM_ABI
constexpr void __set(_Tp&& __value) { __value_.emplace(_VSTD::move(__value)); }
template<class _Other>
_LIBCPP_HIDE_FROM_ABI
constexpr _Tp& __emplace_deref(const _Other& __value) {
__value_.reset();
__value_.emplace(*__value);
return *__value_;
}
};
struct __empty_cache { };

View File

@ -659,6 +659,7 @@ module std [system] {
module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" }
module enable_view { private header "__ranges/enable_view.h" }
module iota_view { private header "__ranges/iota_view.h" }
module join_view { private header "__ranges/join_view.h" }
module non_propagating_cache { private header "__ranges/non_propagating_cache.h" }
module ref_view { private header "__ranges/ref_view.h" }
module reverse_view { private header "__ranges/reverse_view.h" }

View File

@ -184,6 +184,11 @@ namespace std::ranges {
template<class W, class Bound>
inline constexpr bool enable_borrowed_range<iota_view<W, Bound>> = true;
// [range.join], join view
template<input_range V>
requires view<V> && input_range<range_reference_t<V>>
class join_view;
}
*/
@ -207,6 +212,7 @@ namespace std::ranges {
#include <__ranges/enable_borrowed_range.h>
#include <__ranges/enable_view.h>
#include <__ranges/iota_view.h>
#include <__ranges/join_view.h>
#include <__ranges/ref_view.h>
#include <__ranges/reverse_view.h>
#include <__ranges/take_view.h>

View File

@ -0,0 +1,16 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: modules-build
// WARNING: This test was generated by 'generate_private_header_tests.py'
// and should not be edited manually.
// expected-error@*:* {{use of private header from outside its module: '__ranges/join_view.h'}}
#include <__ranges/join_view.h>

View File

@ -0,0 +1,62 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr V base() const& requires copy_constructible<V>;
// constexpr V base() &&;
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "types.h"
constexpr bool hasLValueQualifiedBase(auto&& view) {
return requires { view.base(); };
}
constexpr bool test() {
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
{
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView{children});
assert(std::move(jv).base().ptr_ == children);
static_assert(!hasLValueQualifiedBase(jv));
ASSERT_SAME_TYPE(decltype(std::move(jv).base()), ParentView<ChildView>);
}
{
std::ranges::join_view jv(buffer);
assert(jv.base().base() == buffer + 0);
static_assert(hasLValueQualifiedBase(jv));
ASSERT_SAME_TYPE(decltype(jv.base()), std::ranges::ref_view<int [4][4]>);
}
{
const std::ranges::join_view jv(buffer);
assert(jv.base().base() == buffer + 0);
static_assert(hasLValueQualifiedBase(jv));
ASSERT_SAME_TYPE(decltype(jv.base()), std::ranges::ref_view<int [4][4]>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,97 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto begin();
// constexpr auto begin() const;
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "types.h"
constexpr bool test() {
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
{
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView{children});
assert(*jv.begin() == 1111);
}
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 1), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(*jv.begin() == 1111);
}
// Parent is empty.
{
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
std::ranges::join_view jv(ParentView(children, 0));
assert(jv.begin() == jv.end());
}
// Parent size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0])};
std::ranges::join_view jv(ParentView(children, 1));
assert(*jv.begin() == 1111);
}
// Parent and child size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0], 1)};
std::ranges::join_view jv(ParentView(children, 1));
assert(*jv.begin() == 1111);
}
// Parent size is one child is empty
{
CopyableChild children[1] = {CopyableChild(buffer[0], 0)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.begin() == jv.end());
}
// Has all empty children.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 0), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(jv.begin() == jv.end());
}
// First child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(*jv.begin() == 1111);
}
// Last child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 4), CopyableChild(buffer[2], 4), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(*jv.begin() == 1111);
}
{
std::ranges::join_view jv(buffer);
assert(*jv.begin() == 1111);
}
{
const std::ranges::join_view jv(buffer);
assert(*jv.begin() == 1111);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,72 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template<class R>
// explicit join_view(R&&) -> join_view<views::all_t<R>>;
#include <ranges>
#include "test_iterators.h"
template<class T>
struct View : std::ranges::view_base {
// All friends here are defined to prevent GCC warnings.
friend T* begin(View&) { return nullptr; }
friend T* begin(View const&) { return nullptr; }
friend sentinel_wrapper<T*> end(View&) { return sentinel_wrapper<T*>(nullptr); }
friend sentinel_wrapper<T*> end(View const&) { return sentinel_wrapper<T*>(nullptr); }
};
template<class T>
struct Range {
friend T* begin(Range&) { return nullptr; }
friend T* begin(Range const&) { return nullptr; }
friend sentinel_wrapper<T*> end(Range&) { return sentinel_wrapper<T*>(nullptr); }
friend sentinel_wrapper<T*> end(Range const&) { return sentinel_wrapper<T*>(nullptr); }
};
template<class T>
struct BorrowedRange {
friend T* begin(BorrowedRange&) { return nullptr; }
friend T* begin(BorrowedRange const&) { return nullptr; }
friend sentinel_wrapper<T*> end(BorrowedRange&) { return sentinel_wrapper<T*>(nullptr); }
friend sentinel_wrapper<T*> end(BorrowedRange const&) { return sentinel_wrapper<T*>(nullptr); }
};
template<>
inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange<BorrowedRange<int>>> = true;
void testCTAD() {
View<View<int>> v;
Range<Range<int>> r;
BorrowedRange<BorrowedRange<int>> br;
static_assert(std::same_as<
decltype(std::ranges::join_view(v)),
std::ranges::join_view<View<View<int>>>
>);
static_assert(std::same_as<
decltype(std::ranges::join_view(r)),
std::ranges::join_view<std::ranges::ref_view<Range<Range<int>>>>
>);
// std::ranges::join_view(std::move(r)) invalid. RValue range must be borrowed.
static_assert(std::same_as<
decltype(std::ranges::join_view(br)),
std::ranges::join_view<std::ranges::ref_view<BorrowedRange<BorrowedRange<int>>>>
>);
static_assert(std::same_as<
decltype(std::ranges::join_view(std::move(br))),
std::ranges::join_view<std::ranges::subrange<BorrowedRange<int> *,
sentinel_wrapper<BorrowedRange<int> *>,
std::ranges::subrange_kind::unsized>>
>);
}

View File

@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template<class R>
// explicit join_view(R&&) -> join_view<views::all_t<R>>;
// Tests that the deduction guide is explicit.
#include <ranges>
#include "test_iterators.h"
template<class T>
struct Range {
friend T* begin(Range&) { return nullptr; }
friend T* begin(Range const&) { return nullptr; }
friend sentinel_wrapper<T*> end(Range&) { return sentinel_wrapper<T*>(nullptr); }
friend sentinel_wrapper<T*> end(Range const&) { return sentinel_wrapper<T*>(nullptr); }
};
void testExplicitCTAD() {
Range<Range<int>> r;
std::ranges::join_view v = r; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'join_view'}}
}

View File

@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr explicit join_view(V base);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "types.h"
constexpr bool test() {
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
{
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView{children});
assert(std::move(jv).base().ptr_ == children);
}
{
std::ranges::join_view jv(buffer);
assert(jv.base().base() == buffer + 0);
}
{
// Test explicitness.
static_assert( std::is_constructible_v<std::ranges::join_view<ParentView<ChildView>>, ParentView<ChildView>>);
static_assert(!std::is_convertible_v<std::ranges::join_view<ParentView<ChildView>>, ParentView<ChildView>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,37 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// join_view() requires default_initializable<V> = default;
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "types.h"
constexpr bool test() {
std::ranges::join_view<ParentView<ChildView>> jv;
assert(std::move(jv).base().ptr_ == globalChildren);
static_assert( std::default_initializable<std::ranges::join_view<ParentView<ChildView>>>);
static_assert(!std::default_initializable<std::ranges::join_view<CopyableParent>>);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,120 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto end();
// constexpr auto end() const;
#include <cassert>
#include <ranges>
#include <type_traits>
#include "test_macros.h"
#include "types.h"
constexpr bool test() {
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
// Non const common, forward range.
{
std::ranges::join_view jv(buffer);
assert(jv.end() == std::ranges::next(jv.begin(), 16));
static_assert(std::same_as<decltype(jv.end()), decltype(jv.begin())>);
}
// Non const not common, input range.
{
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
static_assert(!std::same_as<decltype(jv.end()), decltype(jv.begin())>);
}
// Const common, forward range.
{
const std::ranges::join_view jv(buffer);
assert(jv.end() == std::ranges::next(jv.begin(), 16));
static_assert(std::same_as<decltype(jv.end()), decltype(jv.begin())>);
}
// Const not common, input range.
{
static_assert(std::is_reference_v<std::ranges::range_reference_t<const CopyableParent>>);
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
const auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
static_assert(!std::same_as<decltype(jv.end()), decltype(jv.begin())>);
}
// Has some empty children.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 1), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 5));
}
// Parent is empty.
{
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
std::ranges::join_view jv(ParentView(children, 0));
assert(jv.end() == jv.begin());
}
// Parent size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0])};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.end() == std::ranges::next(jv.begin(), 4));
}
// Parent and child size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0], 1)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.end() == std::ranges::next(jv.begin()));
}
// Parent size is one child is empty
{
CopyableChild children[1] = {CopyableChild(buffer[0], 0)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.end() == jv.begin());
}
// Has all empty children.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 0), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == jv.begin());
}
// First child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 4));
}
// Last child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 4), CopyableChild(buffer[2], 4), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 12));
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// General tests for join_view. This file does not test anything specifically.
#include <algorithm>
#include <cassert>
#include <ranges>
#include <string>
#include <vector>
#include "test_macros.h"
#include "types.h"
template<class R, class I>
bool isEqual(R &r, I i) {
for (auto e : r)
if (e != *i++)
return false;
return true;
}
int main(int, char**) {
{
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
int *flattened = reinterpret_cast<int*>(buffer);
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView(children));
assert(isEqual(jv, flattened));
}
{
std::vector<std::string> vec = {"Hello", ",", " ", "World", "!"};
std::string check = "Hello, World!";
std::ranges::join_view jv(vec);
assert(isEqual(jv, check.begin()));
}
return 0;
}

View File

@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr InnerIter operator->() const
// requires has-arrow<InnerIter> && copyable<InnerIter>;
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
Box buffer[4][4] = {{{1111}, {2222}, {3333}, {4444}}, {{555}, {666}, {777}, {888}}, {{99}, {1010}, {1111}, {1212}}, {{13}, {14}, {15}, {16}}};
{
// Copyable input iterator with arrow.
ValueView<Box> children[4] = {ValueView(buffer[0]), ValueView(buffer[1]), ValueView(buffer[2]), ValueView(buffer[3])};
std::ranges::join_view jv(ValueView<ValueView<Box>>{children});
assert(jv.begin()->x == 1111);
}
{
std::ranges::join_view jv(buffer);
assert(jv.begin()->x == 1111);
}
{
const std::ranges::join_view jv(buffer);
assert(jv.begin()->x == 1111);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,56 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// iterator() requires default_initializable<OuterIter> = default;
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
template<class T>
struct DefaultCtorParent : std::ranges::view_base {
T *ptr_;
constexpr DefaultCtorParent(T *ptr) : ptr_(ptr) {}
constexpr cpp17_input_iterator<T *> begin() { return cpp17_input_iterator<T *>(ptr_); }
constexpr cpp17_input_iterator<const T *> begin() const { return cpp17_input_iterator<const T *>(ptr_); }
constexpr T *end() { return ptr_ + 4; }
constexpr const T *end() const { return ptr_ + 4; }
};
template<class T>
constexpr bool operator==(const cpp17_input_iterator<T*> &lhs, const T *rhs) { return lhs.base() == rhs; }
template<class T>
constexpr bool operator==(const T *lhs, const cpp17_input_iterator<T*> &rhs) { return rhs.base() == lhs; }
constexpr bool test() {
using Base = DefaultCtorParent<ChildView>;
// Note, only the outer iterator is default_initializable:
static_assert( std::default_initializable<std::ranges::iterator_t<Base>>);
static_assert(!std::default_initializable<std::ranges::iterator_t<std::ranges::range_reference_t<Base>>>);
std::ranges::iterator_t<std::ranges::join_view<Base>> iter1;
(void) iter1;
static_assert(!std::default_initializable<std::ranges::iterator_t<std::ranges::join_view<ParentView<ChildView>>>>);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator(iterator<!Const> i);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
std::ranges::join_view jv(CopyableParent{children});
auto iter1 = jv.begin();
std::ranges::iterator_t<const decltype(jv)> iter2 = iter1;
assert(iter1 == iter2);
// We cannot create a non-const iterator from a const iterator.
static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator(Parent& parent, OuterIter outer);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
CopyableParent parent{children};
std::ranges::join_view jv(parent);
std::ranges::iterator_t<decltype(jv)> iter(jv, std::ranges::begin(parent));
assert(*iter == 1);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator& operator--();
// constexpr iterator operator--(int);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
{
// outer == ranges::end
std::ranges::join_view jv(buffer);
auto iter = std::next(jv.begin(), 16);
for (int i = 16; i != 0; --i) {
assert(*--iter == i);
}
}
{
// outer == ranges::end
std::ranges::join_view jv(buffer);
auto iter = std::next(jv.begin(), 13);
for (int i = 13; i != 0; --i) {
assert(*--iter == i);
}
}
{
// outer != ranges::end
std::ranges::join_view jv(buffer);
auto iter = std::next(jv.begin(), 12);
for (int i = 12; i != 0; --i) {
assert(*--iter == i);
}
}
{
// outer != ranges::end
std::ranges::join_view jv(buffer);
auto iter = std::next(jv.begin());
for (int i = 1; i != 0; --i) {
assert(*--iter == i);
}
}
{
int small[2][1] = {{1}, {2}};
std::ranges::join_view jv(small);
auto iter = std::next(jv.begin(), 2);
for (int i = 2; i != 0; --i) {
assert(*--iter == i);
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr bool operator==(const iterator& x, const iterator& y);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
std::ranges::join_view jv(buffer);
auto iter1 = jv.begin();
auto iter2 = jv.begin();
assert(iter1 == iter2);
iter1++;
assert(iter1 != iter2);
iter2++;
assert(iter1 == iter2);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,160 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator& operator++();
// constexpr void operator++(int);
// constexpr iterator operator++(int);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
// This way if we read past end we'll catch the error.
int buffer1[2][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};
int dummy = 42;
(void) dummy;
int buffer2[2][4] = {{9, 10, 11, 12}, {13, 14, 15, 16}};
// operator++(int);
{
std::ranges::join_view jv(buffer1);
auto iter = jv.begin();
for (int i = 1; i < 9; ++i) {
assert(*iter++ == i);
}
}
{
ValueView<int> children[4] = {ValueView(buffer1[0]), ValueView(buffer1[1]), ValueView(buffer2[0]), ValueView(buffer2[1])};
std::ranges::join_view jv(ValueView<ValueView<int>>{children});
auto iter = jv.begin();
for (int i = 1; i < 17; ++i) {
assert(*iter == i);
iter++;
}
ASSERT_SAME_TYPE(decltype(iter++), void);
}
{
std::ranges::join_view jv(buffer1);
auto iter = std::next(jv.begin(), 7);
assert(*iter++ == 8);
assert(iter == jv.end());
}
{
int small[2][1] = {{1}, {2}};
std::ranges::join_view jv(small);
auto iter = jv.begin();
for (int i = 1; i < 3; ++i) {
assert(*iter++ == i);
}
}
// Has some empty children.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 1), CopyableChild(buffer2[1], 0)};
auto jv = std::ranges::join_view(ParentView(children));
auto iter = jv.begin();
assert(*iter == 1); iter++;
assert(*iter == 2); iter++;
assert(*iter == 3); iter++;
assert(*iter == 4); iter++;
assert(*iter == 9); iter++;
assert(iter == jv.end());
}
// Parent is empty.
{
CopyableChild children[4] = {CopyableChild(buffer1[0]), CopyableChild(buffer1[1]), CopyableChild(buffer2[0]), CopyableChild(buffer2[1])};
std::ranges::join_view jv(ParentView(children, 0));
assert(jv.begin() == jv.end());
}
// Parent size is one.
{
CopyableChild children[1] = {CopyableChild(buffer1[0])};
std::ranges::join_view jv(ParentView(children, 1));
auto iter = jv.begin();
assert(*iter == 1); iter++;
assert(*iter == 2); iter++;
assert(*iter == 3); iter++;
assert(*iter == 4); iter++;
assert(iter == jv.end());
}
// Parent and child size is one.
{
CopyableChild children[1] = {CopyableChild(buffer1[0], 1)};
std::ranges::join_view jv(ParentView(children, 1));
auto iter = jv.begin();
assert(*iter == 1); iter++;
assert(iter == jv.end());
}
// Parent size is one child is empty
{
CopyableChild children[1] = {CopyableChild(buffer1[0], 0)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.begin() == jv.end());
}
// Has all empty children.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 0), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 0), CopyableChild(buffer2[1], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.begin() == jv.end());
}
// First child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 0), CopyableChild(buffer2[1], 0)};
auto jv = std::ranges::join_view(ParentView(children));
auto iter = jv.begin();
assert(*iter == 1); iter++;
assert(*iter == 2); iter++;
assert(*iter == 3); iter++;
assert(*iter == 4); iter++;
assert(iter == jv.end());
}
// Last child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 4), CopyableChild(buffer2[0], 4), CopyableChild(buffer2[1], 0)};
auto jv = std::ranges::join_view(ParentView(children));
auto iter = jv.begin();
for (int i = 1; i < 13; ++i) {
assert(*iter == i);
iter++;
}
}
// operator++();
{
std::ranges::join_view jv(buffer1);
auto iter = jv.begin();
for (int i = 2; i < 9; ++i) {
assert(*++iter == i);
}
}
{
ValueView<int> children[4] = {ValueView(buffer1[0]), ValueView(buffer1[1]), ValueView(buffer2[0]), ValueView(buffer2[1])};
std::ranges::join_view jv(ValueView<ValueView<int>>{children});
auto iter = jv.begin();
for (int i = 2; i < 17; ++i) {
assert(*++iter == i);
}
ASSERT_SAME_TYPE(decltype(++iter), decltype(iter)&);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr decltype(auto) iter_move(const iterator& i);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
std::ranges::join_view jv(buffer);
assert(std::ranges::iter_move(jv.begin()) == 1);
ASSERT_SAME_TYPE(decltype(std::ranges::iter_move(jv.begin())), int&&);
static_assert(noexcept(std::ranges::iter_move(std::declval<decltype(jv.begin())>())));
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr void iter_swap(const iterator& x, const iterator& y);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
std::ranges::join_view jv(buffer);
auto iter1 = jv.begin();
auto iter2 = std::next(jv.begin());
assert(*iter1 == 1);
assert(*iter2 == 2);
std::ranges::swap(iter1, iter2);
assert(*iter1 == 2);
assert(*iter2 == 1);
static_assert(noexcept(std::ranges::iter_swap(iter1, iter2)));
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// Iterator traits and member typedefs in join_view::<iterator>.
#include <ranges>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"
template<class T>
struct ForwardView : std::ranges::view_base {
friend forward_iterator<T*> begin(ForwardView&) { return forward_iterator<T*>(nullptr); }
friend forward_iterator<T*> begin(ForwardView const&) { return forward_iterator<T*>(nullptr); }
friend forward_iterator<T*> end(ForwardView&) { return forward_iterator<T*>(nullptr); }
friend forward_iterator<T*> end(ForwardView const&) { return forward_iterator<T*>(nullptr); }
};
template<class T>
struct InputView : std::ranges::view_base {
friend cpp17_input_iterator<T*> begin(InputView&) { return cpp17_input_iterator<T*>(nullptr); }
friend cpp17_input_iterator<T*> begin(InputView const&) { return cpp17_input_iterator<T*>(nullptr); }
friend cpp17_input_iterator<T*> end(InputView&) { return cpp17_input_iterator<T*>(nullptr); }
friend cpp17_input_iterator<T*> end(InputView const&) { return cpp17_input_iterator<T*>(nullptr); }
};
template<class T>
concept HasIterCategory = requires { typename T::iterator_category; };
void test() {
{
int buffer[4][4];
std::ranges::join_view jv(buffer);
using Iter = std::ranges::iterator_t<decltype(jv)>;
ASSERT_SAME_TYPE(Iter::iterator_concept, std::bidirectional_iterator_tag);
ASSERT_SAME_TYPE(Iter::iterator_category, std::bidirectional_iterator_tag);
ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t);
ASSERT_SAME_TYPE(Iter::value_type, int);
}
{
using Iter = std::ranges::iterator_t<std::ranges::join_view<ForwardView<ForwardView<int>>>>;
ASSERT_SAME_TYPE(Iter::iterator_concept, std::forward_iterator_tag);
ASSERT_SAME_TYPE(Iter::iterator_category, std::forward_iterator_tag);
ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t);
ASSERT_SAME_TYPE(Iter::value_type, int);
}
{
using Iter = std::ranges::iterator_t<std::ranges::join_view<InputView<InputView<int>>>>;
ASSERT_SAME_TYPE(Iter::iterator_concept, std::input_iterator_tag);
static_assert(!HasIterCategory<Iter>);
ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t);
ASSERT_SAME_TYPE(Iter::value_type, int);
}
}

View File

@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr decltype(auto) operator*() const;
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
{
std::ranges::join_view jv(buffer);
auto iter = jv.begin();
for (int i = 1; i < 17; ++i) {
assert(*iter++ == i);
}
}
{
std::ranges::join_view jv(buffer);
auto iter = std::next(jv.begin(), 15);
assert(*iter++ == 16);
assert(iter == jv.end());
}
{
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView(children));
auto iter = jv.begin();
for (int i = 1; i < 17; ++i) {
assert(*iter == i);
++iter;
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// sentinel() = default;
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
std::ranges::sentinel_t<std::ranges::join_view<CopyableParent>> sent;
(void) sent;
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr sentinel(sentinel<!Const> s);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
std::ranges::join_view jv(CopyableParent{children});
auto sent1 = jv.end();
std::ranges::sentinel_t<const decltype(jv)> sent2 = sent1;
(void) sent2; // We can't really do anything with these sentinels now :/
// We cannot create a non-const iterator from a const iterator.
static_assert(!std::constructible_from<decltype(sent1), decltype(sent2)>);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,45 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr explicit sentinel(Parent& parent);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
CopyableParent parent{children};
std::ranges::join_view jv(parent);
std::ranges::sentinel_t<decltype(jv)> sent(jv);
assert(sent == std::ranges::next(jv.begin(), 16));
return true;
}
int main(int, char**) {
test();
static_assert(test());
{
// Test explicitness.
using Parent = std::ranges::join_view<ParentView<ChildView>>;
static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
}
return 0;
}

View File

@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// 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
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template<bool OtherConst>
// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
#include <cassert>
#include <ranges>
#include "test_macros.h"
#include "../types.h"
constexpr bool test() {
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
{
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
}
{
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
const auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
}
{
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
const std::ranges::join_view jvc(CopyableParent{children});
std::ranges::join_view jv(CopyableParent{children});
assert(jvc.end() == std::ranges::next(jv.begin(), 16));
assert( jv.end() == std::ranges::next(jvc.begin(), 16));
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,141 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H
#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H
#include <concepts>
#include "test_macros.h"
#include "test_iterators.h"
int globalBuffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
struct ChildView : std::ranges::view_base {
int *ptr_;
constexpr ChildView(int *ptr = globalBuffer[0]) : ptr_(ptr) {}
ChildView(const ChildView&) = delete;
ChildView(ChildView&&) = default;
ChildView& operator=(const ChildView&) = delete;
ChildView& operator=(ChildView&&) = default;
constexpr cpp20_input_iterator<int *> begin() { return cpp20_input_iterator<int *>(ptr_); }
constexpr cpp20_input_iterator<const int *> begin() const { return cpp20_input_iterator<const int *>(ptr_); }
constexpr int *end() { return ptr_ + 4; }
constexpr const int *end() const { return ptr_ + 4; }
};
constexpr bool operator==(const cpp20_input_iterator<int*> &lhs, int* rhs) { return lhs.base() == rhs; }
constexpr bool operator==(int* lhs, const cpp20_input_iterator<int*> &rhs) { return rhs.base() == lhs; }
ChildView globalChildren[4] = {ChildView(globalBuffer[0]), ChildView(globalBuffer[1]), ChildView(globalBuffer[2]), ChildView(globalBuffer[3])};
template<class T>
struct ParentView : std::ranges::view_base {
T *ptr_;
unsigned size_;
constexpr ParentView(T *ptr, unsigned size = 4)
: ptr_(ptr), size_(size) {}
constexpr ParentView(ChildView *ptr = globalChildren, unsigned size = 4)
requires std::same_as<ChildView, T>
: ptr_(ptr), size_(size) {}
ParentView(const ParentView&) = delete;
ParentView(ParentView&&) = default;
ParentView& operator=(const ParentView&) = delete;
ParentView& operator=(ParentView&&) = default;
constexpr cpp20_input_iterator<T *> begin() { return cpp20_input_iterator<T *>(ptr_); }
constexpr cpp20_input_iterator<const T *> begin() const { return cpp20_input_iterator<const T *>(ptr_); }
constexpr T *end() { return ptr_ + size_; }
constexpr const T *end() const { return ptr_ + size_; }
};
template<class T>
constexpr bool operator==(const cpp20_input_iterator<T*> &lhs, T *rhs) { return lhs.base() == rhs; }
template<class T>
constexpr bool operator==(T *lhs, const cpp20_input_iterator<T*> &rhs) { return rhs.base() == lhs; }
struct CopyableChild : std::ranges::view_base {
int *ptr_;
unsigned size_;
constexpr CopyableChild(int *ptr = globalBuffer[0], unsigned size = 4)
: ptr_(ptr), size_(size) {}
constexpr cpp17_input_iterator<int *> begin() { return cpp17_input_iterator<int *>(ptr_); }
constexpr cpp17_input_iterator<const int *> begin() const { return cpp17_input_iterator<const int *>(ptr_); }
constexpr int *end() { return ptr_ + size_; }
constexpr const int *end() const { return ptr_ + size_; }
};
constexpr bool operator==(const cpp17_input_iterator<const int*> &lhs, const int* rhs) { return lhs.base() == rhs; }
constexpr bool operator==(const int* lhs, const cpp17_input_iterator<const int*> &rhs) { return rhs.base() == lhs; }
struct CopyableParent : std::ranges::view_base {
CopyableChild *ptr_;
constexpr CopyableParent(CopyableChild *ptr) : ptr_(ptr) {}
constexpr cpp17_input_iterator<CopyableChild *> begin() { return cpp17_input_iterator<CopyableChild *>(ptr_); }
constexpr cpp17_input_iterator<const CopyableChild *> begin() const { return cpp17_input_iterator<const CopyableChild *>(ptr_); }
constexpr CopyableChild *end() { return ptr_ + 4; }
constexpr const CopyableChild *end() const { return ptr_ + 4; }
};
constexpr bool operator==(const cpp17_input_iterator<const CopyableChild*> &lhs, const CopyableChild *rhs) { return lhs.base() == rhs; }
constexpr bool operator==(const CopyableChild *lhs, const cpp17_input_iterator<const CopyableChild*> &rhs) { return rhs.base() == lhs; }
struct Box { int x; };
template<class T>
struct InputValueIter {
typedef std::input_iterator_tag iterator_category;
typedef T value_type;
typedef int difference_type;
typedef T reference;
T *ptr_;
constexpr InputValueIter(T *ptr) : ptr_(ptr) {}
constexpr T operator*() const { return std::move(*ptr_); }
constexpr void operator++(int) { ++ptr_; }
constexpr InputValueIter& operator++() { ++ptr_; return *this; }
constexpr T *operator->() { return ptr_; }
};
template<class T>
constexpr bool operator==(const InputValueIter<T> &lhs, const T* rhs) { return lhs.ptr_ == rhs; }
template<class T>
constexpr bool operator==(const T* lhs, const InputValueIter<T> &rhs) { return rhs.ptr_ == lhs; }
template<class T>
struct ValueView : std::ranges::view_base {
InputValueIter<T> ptr_;
constexpr ValueView(T *ptr) : ptr_(ptr) {}
constexpr ValueView(ValueView &&other)
: ptr_(other.ptr_) { other.ptr_.ptr_ = nullptr; }
constexpr ValueView& operator=(ValueView &&other) {
ptr_ = other.ptr_;
other.ptr_ = InputValueIter<T>(nullptr);
return *this;
}
ValueView(const ValueView&) = delete;
ValueView& operator=(const ValueView&) = delete;
constexpr InputValueIter<T> begin() { return ptr_; }
constexpr const InputValueIter<T> begin() const { return ptr_; }
constexpr T *end() { return ptr_.ptr_ + 4; }
constexpr const T *end() const { return ptr_.ptr_ + 4; }
};
#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H

View File

@ -914,6 +914,13 @@ private:
difference_type stride_displacement_ = 0;
};
template<class T, class U>
concept sentinel_for_base = requires(U const& u) {
u.base();
requires std::input_or_output_iterator<std::remove_cvref_t<decltype(u.base())>>;
requires std::equality_comparable_with<T, decltype(u.base())>;
};
template <std::input_or_output_iterator I>
class sentinel_wrapper {
public:
@ -927,6 +934,12 @@ public:
constexpr const I& base() const& { return base_; }
constexpr I base() && { return std::move(base_); }
template<std::input_or_output_iterator I2>
requires sentinel_for_base<I, I2>
constexpr bool operator==(I2 const& other) const {
return base_ == other.base();
}
private:
I base_ = I();
};

View File

@ -62,4 +62,10 @@ struct test_view : std::ranges::view_base {
sentinel end() const;
};
template<template<class...> class I, class R>
constexpr auto make_archetype_range(R&& r) {
return std::ranges::subrange(I(std::ranges::begin(r)), sentinel_wrapper(std::ranges::end(r)));
}
#endif // LIBCXX_TEST_SUPPORT_TEST_RANGE_H