1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17 10 // UNSUPPORTED: libcpp-has-no-incomplete-ranges 11 12 // template <input_range Range> 13 // requires constructible_from<View, views::all_t<Range>> && 14 // constructible_from<Pattern, single_view<range_value_t<Range>>> 15 // constexpr lazy_split_view(Range&& r, range_value_t<Range> e); 16 17 #include <ranges> 18 19 #include <cassert> 20 #include <string> 21 #include <string_view> 22 #include <type_traits> 23 #include <utility> 24 #include "types.h" 25 26 struct ElementWithCounting { 27 int* times_copied = nullptr; 28 int* times_moved = nullptr; 29 30 constexpr ElementWithCounting(int& copies_ctr, int& moves_ctr) : times_copied(&copies_ctr), times_moved(&moves_ctr) {} 31 32 constexpr ElementWithCounting(const ElementWithCounting& rhs) 33 : times_copied(rhs.times_copied) 34 , times_moved(rhs.times_moved) { 35 ++(*times_copied); 36 } 37 constexpr ElementWithCounting(ElementWithCounting&& rhs) 38 : times_copied(rhs.times_copied) 39 , times_moved(rhs.times_moved) { 40 ++(*times_moved); 41 } 42 43 constexpr bool operator==(const ElementWithCounting&) const { return true; } 44 }; 45 46 struct RangeWithCounting { 47 using value_type = ElementWithCounting; 48 49 int* times_copied = nullptr; 50 int* times_moved = nullptr; 51 52 constexpr RangeWithCounting(int& copies_ctr, int& moves_ctr) : times_copied(&copies_ctr), times_moved(&moves_ctr) {} 53 54 constexpr RangeWithCounting(const RangeWithCounting& rhs) 55 : times_copied(rhs.times_copied) 56 , times_moved(rhs.times_moved) { 57 ++(*times_copied); 58 } 59 constexpr RangeWithCounting(RangeWithCounting&& rhs) 60 : times_copied(rhs.times_copied) 61 , times_moved(rhs.times_moved) { 62 ++(*times_moved); 63 } 64 65 constexpr const ElementWithCounting* begin() const { return nullptr; } 66 constexpr const ElementWithCounting* end() const { return nullptr; } 67 68 constexpr RangeWithCounting& operator=(const RangeWithCounting&) = default; 69 constexpr RangeWithCounting& operator=(RangeWithCounting&&) = default; 70 constexpr bool operator==(const RangeWithCounting&) const { return true; } 71 }; 72 static_assert( std::ranges::forward_range<RangeWithCounting>); 73 static_assert(!std::ranges::view<RangeWithCounting>); 74 75 struct StrView : std::ranges::view_base { 76 std::string_view buffer_; 77 constexpr explicit StrView() = default; 78 constexpr StrView(const char* ptr) : buffer_(ptr) {} 79 // Intentionally don't forward to range constructor for std::string_view since 80 // this test needs to work on C++20 as well and the range constructor is only for 81 // C++23 and later. 82 template <std::ranges::range R> 83 constexpr StrView(R&& r) : buffer_(r.begin(), r.end()) {} 84 constexpr const char* begin() const { return buffer_.begin(); } 85 constexpr const char* end() const { return buffer_.end(); } 86 constexpr bool operator==(const StrView& rhs) const { return buffer_ == rhs.buffer_; } 87 }; 88 static_assert( std::ranges::random_access_range<StrView>); 89 static_assert( std::ranges::view<StrView>); 90 static_assert( std::is_copy_constructible_v<StrView>); 91 92 constexpr bool test() { 93 { 94 using V = std::ranges::lazy_split_view<StrView, StrView>; 95 96 // Calling the constructor with `(std::string, range_value_t)`. 97 { 98 std::string input; 99 V v(input, ' '); 100 assert(v.base() == input); 101 } 102 103 // Calling the constructor with `(StrView, range_value_t)`. 104 { 105 StrView input("abc def"); 106 V v(input, ' '); 107 assert(v.base() == input); 108 } 109 110 struct Empty {}; 111 static_assert(!std::is_constructible_v<V, Empty, std::string_view>); 112 static_assert(!std::is_constructible_v<V, std::string_view, Empty>); 113 } 114 115 // Make sure the arguments are moved, not copied. 116 { 117 using Range = RangeWithCounting; 118 using Element = ElementWithCounting; 119 using Pattern = std::ranges::single_view<Element>; 120 121 // Arguments are lvalues. 122 { 123 using View = std::ranges::ref_view<Range>; 124 125 int range_copied = 0, range_moved = 0, element_copied = 0, element_moved = 0; 126 Range range(range_copied, range_moved); 127 Element element(element_copied, element_moved); 128 129 std::ranges::lazy_split_view<View, Pattern> v(range, element); 130 assert(range_copied == 0); // `ref_view` does neither copy... 131 assert(range_moved == 0); // ...nor move the element. 132 assert(element_copied == 1); // The element is copied into the argument... 133 assert(element_moved == 1); // ...and moved into the member variable. 134 } 135 136 // Arguments are rvalues. 137 { 138 using View = std::ranges::owning_view<Range>; 139 140 int range_copied = 0, range_moved = 0, element_copied = 0, element_moved = 0; 141 std::ranges::lazy_split_view<View, Pattern> v( 142 Range(range_copied, range_moved), Element(element_copied, element_moved)); 143 assert(range_copied == 0); 144 assert(range_moved == 1); // `owning_view` moves the given argument. 145 assert(element_copied == 0); 146 assert(element_moved == 1); 147 } 148 } 149 150 return true; 151 } 152 153 int main(int, char**) { 154 test(); 155 static_assert(test()); 156 157 return 0; 158 } 159