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 // <memory>
12 //
13 // template<input_iterator I, sentinel-for<I> S1, nothrow-forward-iterator O, nothrow-sentinel-for<O> S2>
14 //   requires constructible_from<iter_value_t<O>, iter_reference_t<I>>
15 // uninitialized_copy_result<I, O> ranges::uninitialized_copy(I ifirst, S1 ilast, O ofirst, S2 olast); // since C++20
16 //
17 // template<input_range IR, nothrow-forward-range OR>
18 //   requires constructible_from<range_value_t<OR>, range_reference_t<IR>>
19 // uninitialized_copy_result<borrowed_iterator_t<IR>, borrowed_iterator_t<OR>> ranges::uninitialized_copy(IR&& in_range, OR&& out_range); // since C++20
20 
21 #include <algorithm>
22 #include <cassert>
23 #include <iterator>
24 #include <memory>
25 #include <ranges>
26 #include <type_traits>
27 
28 #include "../buffer.h"
29 #include "../counted.h"
30 #include "../overload_compare_iterator.h"
31 #include "test_macros.h"
32 #include "test_iterators.h"
33 
34 // TODO(varconst): consolidate the ADL checks into a single file.
35 // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
36 // implementations are allowed to use a different mechanism to achieve this effect, so this check is
37 // libc++-specific.
38 LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_copy)>);
39 
40 static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_copy), int*, int*, long*, long*>);
41 struct NotConvertibleFromInt {};
42 static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_copy), int*, int*, NotConvertibleFromInt*,
43                                    NotConvertibleFromInt*>);
44 
45 int main(int, char**) {
46   // An empty range -- no default constructors should be invoked.
47   {
48     Counted in[] = {Counted()};
49     Buffer<Counted, 1> out;
50     Counted::reset();
51 
52     {
53       auto result = std::ranges::uninitialized_copy(in, in, out.begin(), out.end());
54       assert(Counted::current_objects == 0);
55       assert(Counted::total_objects == 0);
56       assert(Counted::total_copies == 0);
57       assert(result.in == in);
58       assert(result.out == out.begin());
59     }
60 
61     {
62       std::ranges::empty_view<Counted> view;
63       auto result = std::ranges::uninitialized_copy(view, out);
64       assert(Counted::current_objects == 0);
65       assert(Counted::total_objects == 0);
66       assert(Counted::total_copies == 0);
67       assert(result.in == view.begin());
68       assert(result.out == out.begin());
69     }
70 
71     {
72       forward_iterator<Counted*> it(in);
73       std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(it));
74 
75       auto result = std::ranges::uninitialized_copy(range.begin(), range.end(), out.begin(), out.end());
76       assert(Counted::current_objects == 0);
77       assert(Counted::total_objects == 0);
78       assert(Counted::total_copies == 0);
79       assert(result.in == it);
80       assert(result.out == out.begin());
81     }
82 
83     {
84       forward_iterator<Counted*> it(in);
85       std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(it));
86 
87       auto result = std::ranges::uninitialized_copy(range, out);
88       assert(Counted::current_objects == 0);
89       assert(Counted::total_objects == 0);
90       assert(Counted::total_copies == 0);
91       assert(result.in == it);
92       assert(result.out == out.begin());
93     }
94     Counted::reset();
95   }
96 
97   // A range containing several objects, (iter, sentinel) overload.
98   {
99     constexpr int N = 5;
100     Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
101     Buffer<Counted, N> out;
102     Counted::reset();
103 
104     auto result = std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end());
105     ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_copy_result<Counted*, Counted*>);
106 
107     assert(Counted::current_objects == N);
108     assert(Counted::total_objects == N);
109     assert(Counted::total_copies == N);
110     assert(Counted::total_moves == 0);
111 
112     assert(std::equal(in, in + N, out.begin(), out.end()));
113     assert(result.in == in + N);
114     assert(result.out == out.end());
115 
116     std::destroy(out.begin(), out.end());
117   }
118   Counted::reset();
119 
120   // A range containing several objects, (range) overload.
121   {
122     constexpr int N = 5;
123     Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
124     Buffer<Counted, N> out;
125     Counted::reset();
126 
127     std::ranges::subrange range(in, in + N);
128     auto result = std::ranges::uninitialized_copy(range, out);
129     ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_copy_result<Counted*, Counted*>);
130 
131     assert(Counted::current_objects == N);
132     assert(Counted::total_objects == N);
133     assert(Counted::total_copies == N);
134     assert(Counted::total_moves == 0);
135 
136     assert(std::equal(in, in + N, out.begin(), out.end()));
137     assert(result.in == in + N);
138     assert(result.out == out.end());
139 
140     std::destroy(out.begin(), out.end());
141   }
142   Counted::reset();
143 
144   // Using `counted_iterator`.
145   {
146     constexpr int N = 3;
147     Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
148     Buffer<Counted, 5> out;
149     Counted::reset();
150 
151     std::counted_iterator iter(in, N);
152     auto result = std::ranges::uninitialized_copy(iter, std::default_sentinel, out.begin(), out.end());
153 
154     assert(Counted::current_objects == N);
155     assert(Counted::total_objects == N);
156     assert(Counted::total_copies == N);
157     assert(Counted::total_moves == 0);
158     assert(std::equal(in, in + N, out.begin(), out.begin() + N));
159 
160     assert(result.in == iter + N);
161     assert(result.out == out.begin() + N);
162 
163     std::destroy(out.begin(), out.begin() + N);
164   }
165   Counted::reset();
166 
167   // Using `views::counted`.
168   {
169     constexpr int N = 3;
170     Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
171     Buffer<Counted, 5> out;
172     Counted::reset();
173 
174     auto view = std::views::counted(in, N);
175     auto result = std::ranges::uninitialized_copy(view, out);
176 
177     assert(Counted::current_objects == N);
178     assert(Counted::total_objects == N);
179     assert(Counted::total_copies == N);
180     assert(Counted::total_moves == 0);
181     assert(std::equal(in, in + N, out.begin(), out.begin() + N));
182 
183     assert(result.in == view.begin() + N);
184     assert(result.out == out.begin() + N);
185 
186     std::destroy(out.begin(), out.begin() + N);
187   }
188   Counted::reset();
189 
190   // Using `reverse_view`.
191   {
192     constexpr int N = 3;
193     Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
194     Buffer<Counted, 5> out;
195     Counted::reset();
196 
197     std::ranges::subrange range(in, in + N);
198     auto view = std::ranges::views::reverse(range);
199     auto result = std::ranges::uninitialized_copy(view, out);
200 
201     assert(Counted::current_objects == N);
202     assert(Counted::total_objects == N);
203     assert(Counted::total_copies == N);
204     assert(Counted::total_moves == 0);
205 
206     Counted expected[N] = {Counted(3), Counted(2), Counted(1)};
207     assert(std::equal(out.begin(), out.begin() + N, expected, expected + N));
208 
209     assert(result.in == view.begin() + N);
210     assert(result.out == out.begin() + N);
211 
212     std::destroy(out.begin(), out.begin() + N);
213   }
214   Counted::reset();
215 
216   // Any existing values should be overwritten by copy constructors.
217   {
218     constexpr int N = 5;
219     int in[N] = {1, 2, 3, 4, 5};
220     int out[N] = {6, 7, 8, 9, 10};
221     assert(!std::equal(in, in + N, out, out + N));
222 
223     std::ranges::uninitialized_copy(in, in + 1, out, out + N);
224     assert(out[0] == 1);
225     assert(out[1] == 7);
226 
227     std::ranges::uninitialized_copy(in, in + N, out, out + N);
228     assert(std::equal(in, in + N, out, out + N));
229   }
230 
231   // An exception is thrown while objects are being created -- objects not yet overwritten should
232   // stay valid. (iterator, sentinel) overload.
233 #ifndef TEST_HAS_NO_EXCEPTIONS
234   {
235     constexpr int M = 3;
236     constexpr int N = 5;
237     Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
238     Counted out[N] = {Counted(6), Counted(7), Counted(8), Counted(9), Counted(10)};
239     Counted::reset();
240 
241     Counted::throw_on = M; // When constructing out[3].
242     try {
243       std::ranges::uninitialized_copy(in, in + N, out, out + N);
244       assert(false);
245     } catch (...) {
246     }
247     assert(Counted::current_objects == 0);
248     assert(Counted::total_objects == M);
249     assert(Counted::total_copies == M);
250     assert(Counted::total_moves == 0);
251 
252     assert(out[4].value == 10);
253   }
254   Counted::reset();
255 
256   // An exception is thrown while objects are being created -- objects not yet overwritten should
257   // stay valid. (range) overload.
258   {
259     constexpr int M = 3;
260     constexpr int N = 5;
261     Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
262     Counted out[N] = {Counted(6), Counted(7), Counted(8), Counted(9), Counted(10)};
263     Counted::reset();
264 
265     Counted::throw_on = M; // When constructing out[3].
266     try {
267       std::ranges::uninitialized_copy(in, out);
268       assert(false);
269     } catch (...) {
270     }
271     assert(Counted::current_objects == 0);
272     assert(Counted::total_objects == M);
273     assert(Counted::total_copies == M);
274     assert(Counted::total_moves == 0);
275 
276     assert(out[4].value == 10);
277   }
278   Counted::reset();
279 #endif // TEST_HAS_NO_EXCEPTIONS
280 
281   // Conversions, (iter, sentinel) overload.
282   {
283     constexpr int N = 3;
284     int in[N] = {1, 2, 3};
285     Buffer<double, N> out;
286 
287     std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end());
288     assert(std::equal(in, in + N, out.begin(), out.end()));
289   }
290 
291   // Conversions, (range) overload.
292   {
293     constexpr int N = 3;
294     int in[N] = {1, 2, 3};
295     Buffer<double, N> out;
296 
297     std::ranges::uninitialized_copy(in, out);
298     assert(std::equal(in, in + N, out.begin(), out.end()));
299   }
300 
301   // Destination range is shorter than the source range, (iter, sentinel) overload.
302   {
303     constexpr int M = 3;
304     constexpr int N = 5;
305     Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
306     Buffer<Counted, M> out;
307     Counted::reset();
308 
309     auto result = std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end());
310     assert(Counted::current_objects == M);
311     assert(Counted::total_objects == M);
312     assert(Counted::total_copies == M);
313     assert(Counted::total_moves == 0);
314 
315     assert(std::equal(in, in + M, out.begin(), out.end()));
316     assert(result.in == in + M);
317     assert(result.out == out.end());
318   }
319 
320   // Destination range is shorter than the source range, (range) overload.
321   {
322     constexpr int M = 3;
323     constexpr int N = 5;
324     Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
325     Buffer<Counted, M> out;
326     Counted::reset();
327 
328     std::ranges::subrange range(in, in + N);
329     auto result = std::ranges::uninitialized_copy(range, out);
330     assert(Counted::current_objects == M);
331     assert(Counted::total_objects == M);
332     assert(Counted::total_copies == M);
333     assert(Counted::total_moves == 0);
334 
335     assert(std::equal(in, in + M, out.begin(), out.end()));
336     assert(result.in == in + M);
337     assert(result.out == out.end());
338   }
339 
340   // Move-only iterators are supported.
341   {
342     using MoveOnlyIter = cpp20_input_iterator<const int*>;
343     static_assert(!std::is_copy_constructible_v<MoveOnlyIter>);
344 
345     constexpr int N = 3;
346     struct MoveOnlyRange {
347       int buffer[N] = {1, 2, 3};
348       auto begin() const { return MoveOnlyIter(buffer); }
349       auto end() const { return sentinel_wrapper<MoveOnlyIter>(MoveOnlyIter(buffer)); }
350     };
351     static_assert(std::ranges::input_range<MoveOnlyRange>);
352     MoveOnlyRange in;
353 
354     // (iter, sentinel) overload.
355     {
356       Buffer<int, N> out;
357       std::ranges::uninitialized_copy(in.begin(), in.end(), out.begin(), out.end());
358     }
359 
360     // (range) overload.
361     {
362       Buffer<int, N> out;
363       std::ranges::uninitialized_copy(in, out);
364     }
365   }
366 
367   // Test with an iterator that overloads operator== and operator!= as the input and output iterators
368   {
369     using T        = int;
370     using Iterator = overload_compare_iterator<T*>;
371     const int N    = 5;
372 
373     // input
374     {
375       char pool[sizeof(T) * N] = {0};
376       T* p                     = reinterpret_cast<T*>(pool);
377       T* p_end                 = reinterpret_cast<T*>(pool) + N;
378       T array[N]               = {1, 2, 3, 4, 5};
379       std::ranges::uninitialized_copy(Iterator(array), Iterator(array + N), p, p_end);
380       for (int i = 0; i != N; ++i) {
381         assert(array[i] == p[i]);
382       }
383     }
384 
385     // output
386     {
387       char pool[sizeof(T) * N] = {0};
388       T* p                     = reinterpret_cast<T*>(pool);
389       T* p_end                 = reinterpret_cast<T*>(pool) + N;
390       T array[N]               = {1, 2, 3, 4, 5};
391       std::ranges::uninitialized_copy(array, array + N, Iterator(p), Iterator(p_end));
392       for (int i = 0; i != N; ++i) {
393         assert(array[i] == p[i]);
394       }
395     }
396   }
397 
398   return 0;
399 }
400