xref: /llvm-project/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp (revision 120b0bfbf0bade430fa9b19d78025ccd1d6148d0)
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 
11 // template <input_range Range>
12 //   requires constructible_from<View, views::all_t<Range>> &&
13 //             constructible_from<Pattern, single_view<range_value_t<Range>>>
14 // constexpr lazy_split_view(Range&& r, range_value_t<Range> e); // explicit since C++23
15 
16 #include <ranges>
17 
18 #include <cassert>
19 #include <string>
20 #include <string_view>
21 #include <type_traits>
22 #include <utility>
23 
24 #include "test_convertible.h"
25 #include "test_macros.h"
26 #include "types.h"
27 
28 struct ElementWithCounting {
29   int* times_copied = nullptr;
30   int* times_moved  = nullptr;
31 
ElementWithCountingElementWithCounting32   constexpr ElementWithCounting(int& copies_ctr, int& moves_ctr) : times_copied(&copies_ctr), times_moved(&moves_ctr) {}
33 
ElementWithCountingElementWithCounting34   constexpr ElementWithCounting(const ElementWithCounting& rhs)
35       : times_copied(rhs.times_copied), times_moved(rhs.times_moved) {
36     ++(*times_copied);
37   }
ElementWithCountingElementWithCounting38   constexpr ElementWithCounting(ElementWithCounting&& rhs)
39       : times_copied(rhs.times_copied), times_moved(rhs.times_moved) {
40     ++(*times_moved);
41   }
42 
operator ==ElementWithCounting43   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 
RangeWithCountingRangeWithCounting52   constexpr RangeWithCounting(int& copies_ctr, int& moves_ctr) : times_copied(&copies_ctr), times_moved(&moves_ctr) {}
53 
RangeWithCountingRangeWithCounting54   constexpr RangeWithCounting(const RangeWithCounting& rhs)
55       : times_copied(rhs.times_copied), times_moved(rhs.times_moved) {
56     ++(*times_copied);
57   }
RangeWithCountingRangeWithCounting58   constexpr RangeWithCounting(RangeWithCounting&& rhs) : times_copied(rhs.times_copied), times_moved(rhs.times_moved) {
59     ++(*times_moved);
60   }
61 
beginRangeWithCounting62   constexpr const ElementWithCounting* begin() const { return nullptr; }
endRangeWithCounting63   constexpr const ElementWithCounting* end() const { return nullptr; }
64 
65   constexpr RangeWithCounting& operator=(const RangeWithCounting&) = default;
66   constexpr RangeWithCounting& operator=(RangeWithCounting&&)      = default;
operator ==RangeWithCounting67   constexpr bool operator==(const RangeWithCounting&) const { return true; }
68 };
69 static_assert(std::ranges::forward_range<RangeWithCounting>);
70 static_assert(!std::ranges::view<RangeWithCounting>);
71 
72 struct StrView : std::ranges::view_base {
73   std::string_view buffer_;
74   constexpr explicit StrView() = default;
StrViewStrView75   constexpr StrView(const char* ptr) : buffer_(ptr) {}
76   // Intentionally don't forward to range constructor for std::string_view since
77   // this test needs to work on C++20 as well and the range constructor is only for
78   // C++23 and later.
79   template <std::ranges::range R>
StrViewStrView80   constexpr StrView(R&& r) : buffer_(r.begin(), r.end()) {}
beginStrView81   constexpr std::string_view::const_iterator begin() const { return buffer_.begin(); }
endStrView82   constexpr std::string_view::const_iterator end() const { return buffer_.end(); }
operator ==StrView83   constexpr bool operator==(const StrView& rhs) const { return buffer_ == rhs.buffer_; }
84 };
85 static_assert(std::ranges::random_access_range<StrView>);
86 static_assert(std::ranges::view<StrView>);
87 static_assert(std::is_copy_constructible_v<StrView>);
88 
89 // SFINAE tests.
90 
91 #if TEST_STD_VER >= 23
92 
93 static_assert(
94     !test_convertible<std::ranges::lazy_split_view<StrView, StrView>, StrView, std::ranges::range_value_t<StrView>>(),
95     "This constructor must be explicit");
96 
97 #else
98 
99 static_assert(
100     test_convertible<std::ranges::lazy_split_view<StrView, StrView>, StrView, std::ranges::range_value_t<StrView>>(),
101     "This constructor must not be explicit");
102 
103 #endif // TEST_STD_VER >= 23
104 
test()105 constexpr bool test() {
106   {
107     using V = std::ranges::lazy_split_view<StrView, StrView>;
108 
109     // Calling the constructor with `(std::string, range_value_t)`.
110     {
111       std::string input;
112       V v(input, ' ');
113       assert(v.base() == input);
114     }
115 
116     // Calling the constructor with `(StrView, range_value_t)`.
117     {
118       StrView input("abc def");
119       V v(input, ' ');
120       assert(v.base() == input);
121     }
122 
123     struct Empty {};
124     static_assert(!std::is_constructible_v<V, Empty, std::string_view>);
125     static_assert(!std::is_constructible_v<V, std::string_view, Empty>);
126   }
127 
128   // Make sure the arguments are moved, not copied.
129   {
130     using Range   = RangeWithCounting;
131     using Element = ElementWithCounting;
132     using Pattern = std::ranges::single_view<Element>;
133 
134     // Arguments are lvalues.
135     {
136       using View = std::ranges::ref_view<Range>;
137 
138       int range_copied = 0, range_moved = 0, element_copied = 0, element_moved = 0;
139       Range range(range_copied, range_moved);
140       Element element(element_copied, element_moved);
141 
142       std::ranges::lazy_split_view<View, Pattern> v(range, element);
143       assert(range_copied == 0);   // `ref_view` does neither copy...
144       assert(range_moved == 0);    // ...nor move the element.
145       assert(element_copied == 1); // The element is copied into the argument...
146 #ifndef TEST_COMPILER_GCC
147       //https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98995
148       assert(element_moved == 1); // ...and moved into the member variable.
149 #endif
150     }
151 
152     // Arguments are rvalues.
153     {
154       using View = std::ranges::owning_view<Range>;
155 
156       int range_copied = 0, range_moved = 0, element_copied = 0, element_moved = 0;
157       std::ranges::lazy_split_view<View, Pattern> v(
158           Range(range_copied, range_moved), Element(element_copied, element_moved));
159       assert(range_copied == 0);
160       assert(range_moved == 1); // `owning_view` moves the given argument.
161       assert(element_copied == 0);
162 #ifndef TEST_COMPILER_GCC
163       //https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98995
164       assert(element_moved == 1);
165 #endif
166     }
167   }
168 
169   return true;
170 }
171 
main(int,char **)172 int main(int, char**) {
173   test();
174   static_assert(test());
175 
176   return 0;
177 }
178