-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implement ranges::uninitialized_meow #1164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
StephanTLavavej
merged 25 commits into
microsoft:master
from
miscco:ranges_uninitialized_default_construct
Sep 22, 2020
Merged
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
d12c8b0
Implement several specialized memory range algorithms:
miscco 577eab3
Address review comments
miscco 9415328
Remove noexcept case without _Backout as ++_IFirst could throw too
miscco f2d424e
Use early returns everywhere
miscco 3ffbf82
Bring back the specializations as we have nothrow iterators
miscco 580f1ba
Cleanup memset
miscco a995b00
Define and use voidify_iter
miscco e2e3a32
Fix derp
miscco c925844
Only move once
miscco 5f5d1cb
Fix missing increment
miscco b9ba5f7
Use _Iter_ref-t?
miscco 96dee36
Merge branch 'master' into ranges_uninitialized_default_construct
miscco 154ee95
Expand uninitialized_copy test for possibly throwing copy construction
miscco adc4253
unchecked_copy uses _Copy_memmove ...
miscco e4e1eb3
unname unused variable
miscco 8031035
Expand all the tests
miscco 03a412d
Remove unneeded specializations that are not better than _Backout
miscco fec98ef
Address review comments
miscco b1eaed6
Thanks vscode
miscco aaccea9
Add macro-defense parentheses.
StephanTLavavej 7f31e9e
Apply suggestions from code review
miscco 3b7e991
Address review comments
miscco 282f1b3
Saving a file helps...
miscco 1397829
Use _Fill_memset for correctness
StephanTLavavej 6a4aa21
Merge branch 'master' into ranges_uninitialized_default_construct
StephanTLavavej File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/env.lst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| RUNALL_INCLUDE ..\concepts_matrix.lst |
177 changes: 177 additions & 0 deletions
177
tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| #include <algorithm> | ||
| #include <cassert> | ||
| #include <concepts> | ||
| #include <cstdlib> | ||
| #include <memory> | ||
| #include <ranges> | ||
| #include <span> | ||
| #include <utility> | ||
|
|
||
| #include <range_algorithm_support.hpp> | ||
|
|
||
| using namespace std; | ||
|
|
||
| // Validate that uninitialized_copy_result aliases in_out_result | ||
| STATIC_ASSERT(same_as<ranges::uninitialized_copy_result<int, double>, ranges::in_out_result<int, double>>); | ||
|
|
||
| // Validate dangling story | ||
| STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<false>{}, borrowed<false>{})), | ||
| ranges::uninitialized_copy_result<ranges::dangling, ranges::dangling>>); | ||
| STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<false>{}, borrowed<true>{})), | ||
| ranges::uninitialized_copy_result<ranges::dangling, int*>>); | ||
| STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<true>{}, borrowed<false>{})), | ||
| ranges::uninitialized_copy_result<int*, ranges::dangling>>); | ||
| STATIC_ASSERT(same_as<decltype(ranges::uninitialized_copy(borrowed<true>{}, borrowed<true>{})), | ||
| ranges::uninitialized_copy_result<int*, int*>>); | ||
|
|
||
| struct int_wrapper { | ||
| inline static int constructions = 0; | ||
| inline static int destructions = 0; | ||
|
|
||
| static void clear_counts() { | ||
| constructions = 0; | ||
| destructions = 0; | ||
| } | ||
|
|
||
| static constexpr int magic_throwing_val = 29; | ||
| int val = 10; | ||
|
|
||
| int_wrapper() { | ||
| ++constructions; | ||
| } | ||
| int_wrapper(int x) : val{x} { | ||
| ++constructions; | ||
| } | ||
|
|
||
| int_wrapper(const int_wrapper& that) { | ||
| if (that.val == magic_throwing_val) { | ||
| throw magic_throwing_val; | ||
| } | ||
|
|
||
| val = that.val; | ||
| ++constructions; | ||
| } | ||
|
|
||
| ~int_wrapper() { | ||
| ++destructions; | ||
| } | ||
|
|
||
| int_wrapper& operator=(const int_wrapper&) { | ||
| abort(); | ||
miscco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| auto operator<=>(const int_wrapper&) const = default; | ||
| }; | ||
|
|
||
| template <class T, size_t N> | ||
| struct holder { | ||
| STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); | ||
| alignas(T) unsigned char space[N * sizeof(T)]; | ||
|
|
||
| auto as_span() { | ||
| return span<T, N>{reinterpret_cast<T*>(space + 0), N}; | ||
| } | ||
| }; | ||
|
|
||
| template <class R> | ||
| void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy | ||
| for (auto& e : r) { | ||
| destroy_at(&e); | ||
| } | ||
| } | ||
|
|
||
| struct instantiator { | ||
| static constexpr int expected_output[] = {13, 55, 12345}; | ||
| static constexpr int expected_input[] = {13, 55, 12345}; | ||
|
|
||
| template <ranges::input_range R, ranges::forward_range W> | ||
| static void call() { | ||
| using ranges::uninitialized_copy, ranges::uninitialized_copy_result, ranges::equal, ranges::equal_to, | ||
| ranges::iterator_t; | ||
|
|
||
| { // Validate range overload | ||
| int_wrapper input[3] = {13, 55, 12345}; | ||
| R wrapped_input{input}; | ||
| holder<int_wrapper, 3> mem; | ||
| W wrapped_output{mem.as_span()}; | ||
|
|
||
| int_wrapper::clear_counts(); | ||
| const same_as<uninitialized_copy_result<iterator_t<R>, iterator_t<W>>> auto result = | ||
| uninitialized_copy(wrapped_input, wrapped_output); | ||
| assert(int_wrapper::constructions == 3); | ||
| assert(int_wrapper::destructions == 0); | ||
| assert(result.in == wrapped_input.end()); | ||
| assert(result.out == wrapped_output.end()); | ||
| assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); | ||
| assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); | ||
| not_ranges_destroy(wrapped_output); | ||
| assert(int_wrapper::constructions == 3); | ||
| assert(int_wrapper::destructions == 3); | ||
| } | ||
|
|
||
| { // Validate iterator overload | ||
| int_wrapper input[3] = {13, 55, 12345}; | ||
| R wrapped_input{input}; | ||
| holder<int_wrapper, 3> mem; | ||
| W wrapped_output{mem.as_span()}; | ||
|
|
||
| int_wrapper::clear_counts(); | ||
| const same_as<uninitialized_copy_result<iterator_t<R>, iterator_t<W>>> auto result = uninitialized_copy( | ||
| wrapped_input.begin(), wrapped_input.end(), wrapped_output.begin(), wrapped_output.end()); | ||
| assert(int_wrapper::constructions == 3); | ||
| assert(int_wrapper::destructions == 0); | ||
| assert(result.in == wrapped_input.end()); | ||
| assert(result.out == wrapped_output.end()); | ||
| assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); | ||
| assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); | ||
| not_ranges_destroy(wrapped_output); | ||
| assert(int_wrapper::constructions == 3); | ||
| assert(int_wrapper::destructions == 3); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| struct throwing_test { | ||
| static constexpr int expected_input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; | ||
|
|
||
| template <ranges::input_range R, ranges::forward_range W> | ||
| static void call() { | ||
| // Validate only range overload (one is plenty since they both use the same backend) | ||
| int_wrapper input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; | ||
| R wrapped_input{input}; | ||
| holder<int_wrapper, 4> mem; | ||
| W wrapped_output{mem.as_span()}; | ||
|
|
||
| int_wrapper::clear_counts(); | ||
| try { | ||
| (void) ranges::uninitialized_copy(wrapped_input, wrapped_output); | ||
| assert(false); | ||
| } catch (int i) { | ||
| assert(i == int_wrapper::magic_throwing_val); | ||
| } catch (...) { | ||
| assert(false); | ||
| } | ||
| assert(int_wrapper::constructions == 2); | ||
| assert(int_wrapper::destructions == 2); | ||
| assert(ranges::equal(input, expected_input, ranges::equal_to{}, &int_wrapper::val)); | ||
| } | ||
| }; | ||
|
|
||
| template <test::ProxyRef IsProxy> | ||
| using test_input = test::range<test::input, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no, | ||
| test::CanCompare::yes, IsProxy>; | ||
| using test_output = test::range<test::fwd, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no, | ||
| test::CanCompare::yes, test::ProxyRef::no>; | ||
|
|
||
| int main() { | ||
| // The algorithm is oblivious to non-required category, size, difference, and "proxyness" of the input range. It | ||
| // _is_ sensitive to proxyness in that it requires non-proxy references for the output range. | ||
|
|
||
| instantiator::call<test_input<test::ProxyRef::no>, test_output>(); | ||
| instantiator::call<test_input<test::ProxyRef::yes>, test_output>(); | ||
| throwing_test::call<test_input<test::ProxyRef::no>, test_output>(); | ||
| throwing_test::call<test_input<test::ProxyRef::yes>, test_output>(); | ||
| } | ||
4 changes: 4 additions & 0 deletions
4
tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| RUNALL_INCLUDE ..\concepts_matrix.lst |
143 changes: 143 additions & 0 deletions
143
tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| #include <algorithm> | ||
| #include <cassert> | ||
| #include <concepts> | ||
| #include <cstdlib> | ||
| #include <memory> | ||
| #include <ranges> | ||
| #include <span> | ||
| #include <utility> | ||
|
|
||
| #include <range_algorithm_support.hpp> | ||
|
|
||
| using namespace std; | ||
|
|
||
| // Validate that uninitialized_copy_n_result aliases in_out_result | ||
| STATIC_ASSERT(same_as<ranges::uninitialized_copy_n_result<int, double>, ranges::in_out_result<int, double>>); | ||
|
|
||
| struct int_wrapper { | ||
| inline static int constructions = 0; | ||
| inline static int destructions = 0; | ||
|
|
||
| static void clear_counts() { | ||
| constructions = 0; | ||
| destructions = 0; | ||
| } | ||
|
|
||
| static constexpr int magic_throwing_val = 29; | ||
| int val = 10; | ||
|
|
||
| int_wrapper() { | ||
| ++constructions; | ||
| } | ||
| int_wrapper(int x) : val{x} { | ||
| ++constructions; | ||
| } | ||
|
|
||
| int_wrapper(const int_wrapper& that) { | ||
| if (that.val == magic_throwing_val) { | ||
| throw magic_throwing_val; | ||
| } | ||
|
|
||
| val = that.val; | ||
| ++constructions; | ||
| } | ||
|
|
||
| ~int_wrapper() { | ||
| ++destructions; | ||
| } | ||
|
|
||
| int_wrapper& operator=(const int_wrapper&) { | ||
| abort(); | ||
| } | ||
|
|
||
| auto operator<=>(const int_wrapper&) const = default; | ||
| }; | ||
|
|
||
| template <class T, size_t N> | ||
| struct holder { | ||
| STATIC_ASSERT(N < ~size_t{0} / sizeof(T)); | ||
| alignas(T) unsigned char space[N * sizeof(T)]; | ||
|
|
||
| auto as_span() { | ||
| return span<T, N>{reinterpret_cast<T*>(space + 0), N}; | ||
| } | ||
| }; | ||
|
|
||
| template <class R> | ||
| void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy | ||
| for (auto& e : r) { | ||
| destroy_at(&e); | ||
| } | ||
| } | ||
|
|
||
| struct instantiator { | ||
| static constexpr int expected_output[] = {13, 55, 12345}; | ||
| static constexpr int expected_input[] = {13, 55, 12345}; | ||
|
|
||
| template <ranges::input_range Read, ranges::forward_range Write> | ||
| static void call() { | ||
| using ranges::uninitialized_copy_n, ranges::uninitialized_copy_n_result, ranges::equal, ranges::equal_to, | ||
| ranges::iterator_t; | ||
|
|
||
| int_wrapper input[3] = {13, 55, 12345}; | ||
| Read wrapped_input{input}; | ||
| holder<int_wrapper, 3> mem; | ||
| Write wrapped_output{mem.as_span()}; | ||
|
|
||
| int_wrapper::clear_counts(); | ||
| const same_as<uninitialized_copy_n_result<iterator_t<Read>, iterator_t<Write>>> auto result = | ||
| uninitialized_copy_n(wrapped_input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); | ||
| assert(int_wrapper::constructions == 3); | ||
| assert(int_wrapper::destructions == 0); | ||
| assert(result.in == wrapped_input.end()); | ||
| assert(result.out == wrapped_output.end()); | ||
| assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); | ||
| assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); | ||
| not_ranges_destroy(wrapped_output); | ||
| assert(int_wrapper::constructions == 3); | ||
| assert(int_wrapper::destructions == 3); | ||
| } | ||
| }; | ||
|
|
||
| struct throwing_test { | ||
| static constexpr int expected_input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; | ||
|
|
||
| template <ranges::input_range Read, ranges::forward_range Write> | ||
| static void call() { | ||
| int_wrapper input[] = {13, 55, int_wrapper::magic_throwing_val, 12345}; | ||
| Read wrapped_input{input}; | ||
| holder<int_wrapper, 4> mem; | ||
| Write wrapped_output{mem.as_span()}; | ||
|
|
||
| int_wrapper::clear_counts(); | ||
| try { | ||
| (void) ranges::uninitialized_copy_n(wrapped_input.begin(), 4, wrapped_output.begin(), wrapped_output.end()); | ||
| assert(false); | ||
| } catch (int i) { | ||
| assert(i == int_wrapper::magic_throwing_val); | ||
| } catch (...) { | ||
| assert(false); | ||
| } | ||
| assert(int_wrapper::constructions == 2); | ||
| assert(int_wrapper::destructions == 2); | ||
| } | ||
| }; | ||
|
|
||
| template <test::ProxyRef IsProxy> | ||
| using test_input = test::range<test::input, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no, | ||
| test::CanCompare::yes, IsProxy>; | ||
| using test_output = test::range<test::fwd, int_wrapper, test::Sized::no, test::CanDifference::no, test::Common::no, | ||
| test::CanCompare::yes, test::ProxyRef::no>; | ||
|
|
||
| int main() { | ||
| // The algorithm is oblivious to non-required category, size, difference, and "proxyness" of the input range. It | ||
| // _is_ sensitive to proxyness in that it requires non-proxy references for the output range. | ||
|
|
||
| instantiator::call<test_input<test::ProxyRef::no>, test_output>(); | ||
| instantiator::call<test_input<test::ProxyRef::yes>, test_output>(); | ||
| throwing_test::call<test_input<test::ProxyRef::no>, test_output>(); | ||
| throwing_test::call<test_input<test::ProxyRef::yes>, test_output>(); | ||
| } |
4 changes: 4 additions & 0 deletions
4
tests/std/tests/P0896R4_ranges_alg_uninitialized_default_construct/env.lst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
|
||
| RUNALL_INCLUDE ..\concepts_matrix.lst |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.