Skip to content

Commit e1decd0

Browse files
committed
Implement ranges::uninitialized_copy
1 parent 5ad5043 commit e1decd0

File tree

4 files changed

+250
-0
lines changed

4 files changed

+250
-0
lines changed

stl/inc/memory

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,71 @@ namespace ranges {
5656
concept _No_throw_forward_range = _No_throw_input_range<_Rng>
5757
&& _No_throw_forward_iterator<iterator_t<_Rng>>;
5858
// clang-format on
59+
60+
// ALIAS TEMPLATE uninitialized_copy_result
61+
template <class _In, class _Out>
62+
using uninitialized_copy_result = in_out_result<_In, _Out>;
63+
64+
// VARIABLE ranges::uninitialized_copy
65+
class _Uninitialized_copy_fn : private _Not_quite_object {
66+
public:
67+
using _Not_quite_object::_Not_quite_object;
68+
69+
// clang-format off
70+
template <input_iterator _It1, sentinel_for<_It1> _Se1, _No_throw_forward_iterator _It2, _No_throw_sentinel_for<_It2> _Se2>
71+
requires constructible_from<iter_value_t<_It2>, iter_reference_t<_It1>>
72+
uninitialized_copy_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2) const {
73+
// clang-format on
74+
_Adl_verify_range(_First1, _Last1);
75+
_Adl_verify_range(_First2, _Last2);
76+
auto _UResult =
77+
_Uninitialized_copy_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)),
78+
_Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)));
79+
80+
_Seek_wrapped(_First1, _STD move(_UResult.in));
81+
_Seek_wrapped(_First2, _STD move(_UResult.out));
82+
return {_STD move(_First1), _STD move(_First2)};
83+
}
84+
85+
// clang-format off
86+
template <input_range _Rng1, _No_throw_forward_range _Rng2>
87+
requires constructible_from<range_value_t<_Rng2>, range_reference_t<_Rng1>>
88+
uninitialized_copy_result<borrowed_iterator_t<_Rng1>, borrowed_iterator_t<_Rng2>> operator()(
89+
_Rng1&& _Range1, _Rng2&& _Range2) const {
90+
// clang-format on
91+
auto _First1 = _RANGES begin(_Range1);
92+
auto _UResult = _Uninitialized_copy_unchecked(
93+
_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2));
94+
95+
_Seek_wrapped(_First1, _STD move(_UResult.in));
96+
return {_STD move(_First1), _Rewrap_iterator(_Range2, _STD move(_UResult.out))};
97+
}
98+
99+
private:
100+
template <class _It1, class _Se1, class _It2, class _Se2>
101+
_NODISCARD static uninitialized_copy_result<_It1, _It2> _Uninitialized_copy_unchecked(
102+
_It1 _IFirst, const _Se1 _ILast, _It2 _OFirst, const _Se2 _OLast) {
103+
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>);
104+
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>);
105+
_STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It2>);
106+
_STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se2, _It2>);
107+
_STL_INTERNAL_STATIC_ASSERT(constructible_from<iter_value_t<_It2>, iter_reference_t<_It1>>);
108+
109+
if constexpr (is_same_v<_Se1, _It1> && _Ptr_copy_cat<_It1, _It2>::_Really_trivial) {
110+
return _Copy_memmove(_IFirst, _ILast, _OLast);
111+
} else {
112+
_Uninitialized_backout _Backout{_STD move(_OFirst)};
113+
114+
for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) {
115+
_Backout._Emplace_back(*_IFirst);
116+
}
117+
118+
return {_STD move(_IFirst), _Backout._Release()};
119+
}
120+
}
121+
};
122+
123+
inline constexpr _Uninitialized_copy_fn uninitialized_copy{_Not_quite_object::_Construct_tag{}};
59124
} // namespace ranges
60125
#endif // __cpp_lib_concepts
61126

tests/std/test.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ tests\P0896R4_ranges_alg_shuffle
297297
tests\P0896R4_ranges_alg_swap_ranges
298298
tests\P0896R4_ranges_alg_transform_binary
299299
tests\P0896R4_ranges_alg_transform_unary
300+
tests\P0896R4_ranges_alg_uninitialized_copy
300301
tests\P0896R4_ranges_alg_uninitialized_default_construct
301302
tests\P0896R4_ranges_alg_uninitialized_default_construct_n
302303
tests\P0896R4_ranges_alg_uninitialized_fill
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\concepts_matrix.lst
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <algorithm>
5+
#include <cassert>
6+
#include <concepts>
7+
#include <memory>
8+
#include <ranges>
9+
#include <span>
10+
#include <utility>
11+
12+
#include <range_algorithm_support.hpp>
13+
14+
using namespace std;
15+
16+
// Validate that uninitialized_copy_result aliases in_out_result
17+
STATIC_ASSERT(same_as<ranges::uninitialized_copy_result<int, double>, ranges::in_out_result<int, double>>);
18+
19+
// Validate dangling story
20+
STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<false>{}, borrowed<false>{})),
21+
ranges::uninitialized_copy_result<ranges::dangling, ranges::dangling>>);
22+
STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<false>{}, borrowed<true>{})),
23+
ranges::uninitialized_copy_result<ranges::dangling, int*>>);
24+
STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<true>{}, borrowed<false>{})),
25+
ranges::uninitialized_copy_result<int*, ranges::dangling>>);
26+
STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<true>{}, borrowed<true>{})),
27+
ranges::uninitialized_copy_result<int*, int*>>);
28+
29+
struct int_wrapper {
30+
inline static int constructions = 0;
31+
inline static int destructions = 0;
32+
33+
static void clear_counts() {
34+
constructions = 0;
35+
destructions = 0;
36+
}
37+
38+
static constexpr int magic_throwing_val = 29;
39+
int val = 10;
40+
41+
int_wrapper() {
42+
++constructions;
43+
}
44+
int_wrapper(int x) : val{x} {
45+
++constructions;
46+
}
47+
48+
int_wrapper(const int_wrapper& that) {
49+
if (that.val == magic_throwing_val) {
50+
throw magic_throwing_val;
51+
}
52+
53+
val = exchange(that.val, -1);
54+
++constructions;
55+
}
56+
57+
~int_wrapper() {
58+
++destructions;
59+
}
60+
61+
int_wrapper& operator=(const int_wrapper& that) {
62+
if (that.val == magic_throwing_val) {
63+
throw magic_throwing_val;
64+
}
65+
val = exchange(that.val, -1);
66+
return *this;
67+
}
68+
69+
auto operator<=>(const int_wrapper&) const = default;
70+
};
71+
72+
template <class T, size_t N>
73+
struct holder {
74+
STATIC_ASSERT(N < ~size_t{0} / sizeof(T));
75+
alignas(T) unsigned char space[N * sizeof(T)];
76+
77+
auto as_span() {
78+
return span<T, N>{reinterpret_cast<T*>(space + 0), N};
79+
}
80+
};
81+
82+
template <class R>
83+
void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy
84+
for (auto& e : r) {
85+
destroy_at(&e);
86+
}
87+
}
88+
89+
struct instantiator {
90+
static constexpr int expected_output[] = {13, 55, 12345};
91+
static constexpr int expected_input[] = {-1, -1, -1};
92+
93+
template <ranges::input_range R, ranges::forward_range W>
94+
static void call() {
95+
using ranges::uninitialized_copy, ranges::uninitialized_copy_result, ranges::equal, ranges::equal_to,
96+
ranges::iterator_t;
97+
98+
{ // Validate range overload
99+
int_wrapper input[3] = {13, 55, 12345};
100+
R wrapped_input{input};
101+
holder<int_wrapper, 3> mem;
102+
W wrapped_output{mem.as_span()};
103+
104+
int_wrapper::clear_counts();
105+
const same_as<uninitialized_copy_result<iterator_t<R>, iterator_t<W>>> auto result =
106+
uninitialized_copy(wrapped_input, wrapped_output);
107+
assert(int_wrapper::constructions == 3);
108+
assert(int_wrapper::destructions == 0);
109+
assert(result.in == wrapped_input.end());
110+
assert(result.out == wrapped_output.end());
111+
assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val));
112+
assert(equal(input, expected_input, equal_to{}, &int_wrapper::val));
113+
not_ranges_destroy(wrapped_output);
114+
assert(int_wrapper::constructions == 3);
115+
assert(int_wrapper::destructions == 3);
116+
}
117+
118+
{ // Validate iterator overload
119+
int_wrapper input[3] = {13, 55, 12345};
120+
R wrapped_input{input};
121+
holder<int_wrapper, 3> mem;
122+
W wrapped_output{mem.as_span()};
123+
124+
int_wrapper::clear_counts();
125+
const same_as<uninitialized_copy_result<iterator_t<R>, iterator_t<W>>> auto result = uninitialized_copy(
126+
wrapped_input.begin(), wrapped_input.end(), wrapped_output.begin(), wrapped_output.end());
127+
assert(int_wrapper::constructions == 3);
128+
assert(int_wrapper::destructions == 0);
129+
assert(result.in == wrapped_input.end());
130+
assert(result.out == wrapped_output.end());
131+
assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val));
132+
assert(equal(input, expected_input, equal_to{}, &int_wrapper::val));
133+
not_ranges_destroy(wrapped_output);
134+
assert(int_wrapper::constructions == 3);
135+
assert(int_wrapper::destructions == 3);
136+
}
137+
}
138+
};
139+
140+
struct throwing_test {
141+
static constexpr int expected_input[] = {-1, -1, int_wrapper::magic_throwing_val, 12345};
142+
143+
template <ranges::input_range R, ranges::forward_range W>
144+
static void call() {
145+
// Validate only range overload (one is plenty since they both use the same backend)
146+
int_wrapper input[] = {13, 55, int_wrapper::magic_throwing_val, 12345};
147+
R wrapped_input{input};
148+
holder<int_wrapper, 4> mem;
149+
W wrapped_output{mem.as_span()};
150+
151+
int_wrapper::clear_counts();
152+
try {
153+
(void) ranges::uninitialized_copy(wrapped_input, wrapped_output);
154+
assert(false);
155+
} catch (int i) {
156+
assert(i == int_wrapper::magic_throwing_val);
157+
} catch (...) {
158+
assert(false);
159+
}
160+
assert(int_wrapper::constructions == 2);
161+
assert(int_wrapper::destructions == 2);
162+
assert(ranges::equal(input, expected_input, ranges::equal_to{}, &int_wrapper::val));
163+
}
164+
};
165+
166+
template <test::ProxyRef IsProxy>
167+
using test_input = test::range<test::input, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no,
168+
test::CanCompare::yes, IsProxy>;
169+
using test_output = test::range<test::fwd, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no,
170+
test::CanCompare::yes, test::ProxyRef::no>;
171+
172+
int main() {
173+
// The algorithm is oblivious to non-required category, size, difference, and "proxyness" of the input range. It
174+
// _is_ sensitive to proxyness in that it requires non-proxy references for the output range.
175+
176+
instantiator::call<test_input<test::ProxyRef::no>, test_output>();
177+
instantiator::call<test_input<test::ProxyRef::yes>, test_output>();
178+
throwing_test::call<test_input<test::ProxyRef::no>, test_output>();
179+
throwing_test::call<test_input<test::ProxyRef::yes>, test_output>();
180+
}

0 commit comments

Comments
 (0)