Skip to content

Commit 7ee1c3e

Browse files
misccoCaseyCarter
authored andcommitted
Implement several specialized memory range algorithms:
* `ranges::uninitialized_default_construct`, `ranges::uninitialized_default_construct_n` * `ranges::uninitialized_value_construct`, `ranges::uninitialized_value_construct_n` * `ranges::uninitialized_fill`, `ranges::uninitialized_fill_n` * `ranges::uninitialized_copy`, `ranges::uninitialized_copy_n` * `ranges::uninitialized_move_n`
1 parent c9a43aa commit 7ee1c3e

File tree

20 files changed

+1774
-12
lines changed

20 files changed

+1774
-12
lines changed

stl/inc/memory

Lines changed: 520 additions & 12 deletions
Large diffs are not rendered by default.

tests/std/test.lst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,16 @@ tests\P0896R4_ranges_alg_sort
307307
tests\P0896R4_ranges_alg_swap_ranges
308308
tests\P0896R4_ranges_alg_transform_binary
309309
tests\P0896R4_ranges_alg_transform_unary
310+
tests\P0896R4_ranges_alg_uninitialized_copy
311+
tests\P0896R4_ranges_alg_uninitialized_copy_n
312+
tests\P0896R4_ranges_alg_uninitialized_default_construct
313+
tests\P0896R4_ranges_alg_uninitialized_default_construct_n
314+
tests\P0896R4_ranges_alg_uninitialized_fill
315+
tests\P0896R4_ranges_alg_uninitialized_fill_n
310316
tests\P0896R4_ranges_alg_uninitialized_move
317+
tests\P0896R4_ranges_alg_uninitialized_move_n
318+
tests\P0896R4_ranges_alg_uninitialized_value_construct
319+
tests\P0896R4_ranges_alg_uninitialized_value_construct_n
311320
tests\P0896R4_ranges_alg_unique
312321
tests\P0896R4_ranges_alg_unique_copy
313322
tests\P0896R4_ranges_algorithm_machinery
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 = that.val;
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 = that.val;
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[] = {13, 55, 12345};
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[] = {13, 55, 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+
}
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: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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_n_result aliases in_out_result
17+
STATIC_ASSERT(same_as<ranges::uninitialized_copy_n_result<int, double>, ranges::in_out_result<int, double>>);
18+
19+
struct int_wrapper {
20+
inline static int constructions = 0;
21+
inline static int destructions = 0;
22+
23+
static void clear_counts() {
24+
constructions = 0;
25+
destructions = 0;
26+
}
27+
28+
static constexpr int magic_throwing_val = 29;
29+
int val = 10;
30+
31+
int_wrapper() {
32+
++constructions;
33+
}
34+
int_wrapper(int x) : val{x} {
35+
++constructions;
36+
}
37+
38+
int_wrapper(const int_wrapper& that) {
39+
if (that.val == magic_throwing_val) {
40+
throw magic_throwing_val;
41+
}
42+
43+
val = that.val;
44+
++constructions;
45+
}
46+
47+
~int_wrapper() {
48+
++destructions;
49+
}
50+
51+
int_wrapper& operator=(const int_wrapper& that) {
52+
if (that.val == magic_throwing_val) {
53+
throw magic_throwing_val;
54+
}
55+
val = that.val;
56+
return *this;
57+
}
58+
59+
auto operator<=>(const int_wrapper&) const = default;
60+
};
61+
62+
template <class T, size_t N>
63+
struct holder {
64+
STATIC_ASSERT(N < ~size_t{0} / sizeof(T));
65+
alignas(T) unsigned char space[N * sizeof(T)];
66+
67+
auto as_span() {
68+
return span<T, N>{reinterpret_cast<T*>(space + 0), N};
69+
}
70+
};
71+
72+
template <class R>
73+
void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy
74+
for (auto& e : r) {
75+
destroy_at(&e);
76+
}
77+
}
78+
79+
struct instantiator {
80+
static constexpr int expected_output[] = {13, 55, 12345};
81+
static constexpr int expected_input[] = {13, 55, 12345};
82+
83+
template <ranges::input_range Read, ranges::forward_range Write>
84+
static void call() {
85+
using ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, ranges::equal_to,
86+
ranges::iterator_t;
87+
88+
{ // Validate iterator overload
89+
int_wrapper input[3] = {13, 55, 12345};
90+
Read wrapped_input{input};
91+
holder<int_wrapper, 3> mem;
92+
Write wrapped_output{mem.as_span()};
93+
94+
int_wrapper::clear_counts();
95+
const same_as<uninitialized_copy_n_result<iterator_t<Read>, iterator_t<Write>>> auto result =
96+
uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end());
97+
assert(int_wrapper::constructions == 3);
98+
assert(int_wrapper::destructions == 0);
99+
assert(result.in == wrapped_input.end());
100+
assert(result.out == wrapped_output.end());
101+
assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val));
102+
assert(equal(input, expected_input, equal_to{}, &int_wrapper::val));
103+
not_ranges_destroy(wrapped_output);
104+
assert(int_wrapper::constructions == 3);
105+
assert(int_wrapper::destructions == 3);
106+
}
107+
}
108+
};
109+
110+
struct throwing_test {
111+
static constexpr int expected_input[] = {13, 55, int_wrapper::magic_throwing_val, 12345};
112+
113+
template <ranges::input_range Read, ranges::forward_range Write>
114+
static void call() {
115+
int_wrapper input[] = {13, 55, int_wrapper::magic_throwing_val, 12345};
116+
Read wrapped_input{input};
117+
holder<int_wrapper, 4> mem;
118+
Write wrapped_output{mem.as_span()};
119+
120+
int_wrapper::clear_counts();
121+
try {
122+
(void) ranges::uninitialized_copy_n(wrapped_input.begin(), 4, wrapped_output.begin(), wrapped_output.end());
123+
assert(false);
124+
} catch (int i) {
125+
assert(i == int_wrapper::magic_throwing_val);
126+
} catch (...) {
127+
assert(false);
128+
}
129+
assert(int_wrapper::constructions == 2);
130+
assert(int_wrapper::destructions == 2);
131+
}
132+
};
133+
134+
template <test::ProxyRef IsProxy>
135+
using test_input = test::range<test::input, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no,
136+
test::CanCompare::yes, IsProxy>;
137+
using test_output = test::range<test::fwd, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no,
138+
test::CanCompare::yes, test::ProxyRef::no>;
139+
140+
int main() {
141+
// The algorithm is oblivious to non-required category, size, difference. It _is_ sensitive to proxyness in that it
142+
// requires non-proxy references for the input range.
143+
144+
instantiator::call<test_input<test::ProxyRef::no>, test_output>();
145+
instantiator::call<test_input<test::ProxyRef::yes>, test_output>();
146+
throwing_test::call<test_input<test::ProxyRef::no>, test_output>();
147+
throwing_test::call<test_input<test::ProxyRef::yes>, test_output>();
148+
}
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

0 commit comments

Comments
 (0)