Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d12c8b0
Implement several specialized memory range algorithms:
miscco Aug 8, 2020
577eab3
Address review comments
miscco Aug 31, 2020
9415328
Remove noexcept case without _Backout as ++_IFirst could throw too
miscco Sep 1, 2020
f2d424e
Use early returns everywhere
miscco Sep 1, 2020
3ffbf82
Bring back the specializations as we have nothrow iterators
miscco Sep 2, 2020
580f1ba
Cleanup memset
miscco Sep 2, 2020
a995b00
Define and use voidify_iter
miscco Sep 2, 2020
e2e3a32
Fix derp
miscco Sep 2, 2020
c925844
Only move once
miscco Sep 2, 2020
5f5d1cb
Fix missing increment
miscco Sep 4, 2020
b9ba5f7
Use _Iter_ref-t?
miscco Sep 4, 2020
96dee36
Merge branch 'master' into ranges_uninitialized_default_construct
miscco Sep 16, 2020
154ee95
Expand uninitialized_copy test for possibly throwing copy construction
miscco Sep 16, 2020
adc4253
unchecked_copy uses _Copy_memmove ...
miscco Sep 16, 2020
e4e1eb3
unname unused variable
miscco Sep 17, 2020
8031035
Expand all the tests
miscco Sep 17, 2020
03a412d
Remove unneeded specializations that are not better than _Backout
miscco Sep 17, 2020
fec98ef
Address review comments
miscco Sep 17, 2020
b1eaed6
Thanks vscode
miscco Sep 17, 2020
aaccea9
Add macro-defense parentheses.
StephanTLavavej Sep 17, 2020
7f31e9e
Apply suggestions from code review
miscco Sep 18, 2020
3b7e991
Address review comments
miscco Sep 18, 2020
282f1b3
Saving a file helps...
miscco Sep 18, 2020
1397829
Use _Fill_memset for correctness
StephanTLavavej Sep 19, 2020
6a4aa21
Merge branch 'master' into ranges_uninitialized_default_construct
StephanTLavavej Sep 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
714 changes: 616 additions & 98 deletions stl/inc/memory

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -4378,6 +4378,28 @@ _OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _O
return _Copy_memmove(_First.base(), _Last.base(), _Dest);
}

template <class _InIt, class _OutIt>
_OutIt _Copy_memmove_common(_InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) {
const char* const _IFirst_ch = const_cast<const char*>(reinterpret_cast<const volatile char*>(_IFirst));
const char* const _ILast_ch = const_cast<const char*>(reinterpret_cast<const volatile char*>(_ILast));
char* const _OFirst_ch = const_cast<char*>(reinterpret_cast<volatile char*>(_OFirst));
const char* const _OLast_ch = const_cast<const char*>(reinterpret_cast<const volatile char*>(_OLast));
const auto _Count = static_cast<size_t>(min(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch));
_CSTD memmove(_OFirst_ch, _IFirst_ch, _Count);
return reinterpret_cast<_OutIt>(_OFirst_ch + _Count);
}

template <class _InIt, class _OutIt>
_OutIt _Copy_memcpy_common(_InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) {
const char* const _IFirst_ch = const_cast<const char*>(reinterpret_cast<const volatile char*>(_IFirst));
const char* const _ILast_ch = const_cast<const char*>(reinterpret_cast<const volatile char*>(_ILast));
char* const _OFirst_ch = const_cast<char*>(reinterpret_cast<volatile char*>(_OFirst));
const char* const _OLast_ch = const_cast<const char*>(reinterpret_cast<const volatile char*>(_OLast));
const auto _Count = static_cast<size_t>(min(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch));
_CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count);
return reinterpret_cast<_OutIt>(_OFirst_ch + _Count);
}

#if _HAS_IF_CONSTEXPR
// VARIABLE TEMPLATE _Is_vb_iterator
template <class _It, bool _RequiresMutable = false>
Expand Down
9 changes: 9 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,16 @@ tests\P0896R4_ranges_alg_sort
tests\P0896R4_ranges_alg_swap_ranges
tests\P0896R4_ranges_alg_transform_binary
tests\P0896R4_ranges_alg_transform_unary
tests\P0896R4_ranges_alg_uninitialized_copy
tests\P0896R4_ranges_alg_uninitialized_copy_n
tests\P0896R4_ranges_alg_uninitialized_default_construct
tests\P0896R4_ranges_alg_uninitialized_default_construct_n
tests\P0896R4_ranges_alg_uninitialized_fill
tests\P0896R4_ranges_alg_uninitialized_fill_n
tests\P0896R4_ranges_alg_uninitialized_move
tests\P0896R4_ranges_alg_uninitialized_move_n
tests\P0896R4_ranges_alg_uninitialized_value_construct
tests\P0896R4_ranges_alg_uninitialized_value_construct_n
tests\P0896R4_ranges_alg_unique
tests\P0896R4_ranges_alg_unique_copy
tests\P0896R4_ranges_algorithm_machinery
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/env.lst
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
197 changes: 197 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <concepts>
#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*>>);

enum class CanThrowAtCopyConstruction : bool { no, yes };

template <CanThrowAtCopyConstruction CanThrow>
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) noexcept(!static_cast<bool>(CanThrow)) {
if constexpr (CanThrow == CanThrowAtCopyConstruction::yes) {
if (that.val == magic_throwing_val) {
throw magic_throwing_val;
}
}

val = that.val;
++constructions;
}

~int_wrapper() {
++destructions;
}

int_wrapper& operator=(const int_wrapper&) {
// Shall never be used as we construct in place
throw magic_throwing_val + 1;
}

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);
}
}

template <CanThrowAtCopyConstruction CanThrow>
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;
using wrapper = int_wrapper<CanThrow>;

{ // Validate range overload
wrapper input[3] = {13, 55, 12345};
R wrapped_input{input};
holder<wrapper, 3> mem;
W wrapped_output{mem.as_span()};

wrapper::clear_counts();
const same_as<uninitialized_copy_result<iterator_t<R>, iterator_t<W>>> auto result =
uninitialized_copy(wrapped_input, wrapped_output);
assert(wrapper::constructions == 3);
assert(wrapper::destructions == 0);
assert(result.in == wrapped_input.end());
assert(result.out == wrapped_output.end());
assert(equal(wrapped_output, expected_output, equal_to{}, &wrapper::val));
assert(equal(input, expected_input, equal_to{}, &wrapper::val));
not_ranges_destroy(wrapped_output);
assert(wrapper::constructions == 3);
assert(wrapper::destructions == 3);
}

{ // Validate iterator overload
wrapper input[3] = {13, 55, 12345};
R wrapped_input{input};
holder<wrapper, 3> mem;
W wrapped_output{mem.as_span()};

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(wrapper::constructions == 3);
assert(wrapper::destructions == 0);
assert(result.in == wrapped_input.end());
assert(result.out == wrapped_output.end());
assert(equal(wrapped_output, expected_output, equal_to{}, &wrapper::val));
assert(equal(input, expected_input, equal_to{}, &wrapper::val));
not_ranges_destroy(wrapped_output);
assert(wrapper::constructions == 3);
assert(wrapper::destructions == 3);
}
}
};

struct throwing_test {
using wrapper = int_wrapper<CanThrowAtCopyConstruction::yes>;
static constexpr int expected_input[] = {13, 55, 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)
wrapper input[] = {13, 55, wrapper::magic_throwing_val, 12345};
R wrapped_input{input};
holder<wrapper, 4> mem;
W wrapped_output{mem.as_span()};

wrapper::clear_counts();
try {
(void) ranges::uninitialized_copy(wrapped_input, wrapped_output);
assert(false);
} catch (int i) {
assert(i == wrapper::magic_throwing_val);
} catch (...) {
assert(false);
}
assert(wrapper::constructions == 2);
assert(wrapper::destructions == 2);
assert(ranges::equal(input, expected_input, ranges::equal_to{}, &wrapper::val));
}
};

template <test::ProxyRef IsProxy, CanThrowAtCopyConstruction CanThrow>
using test_input = test::range<test::input, int_wrapper<CanThrow>, test::Sized::no, test::CanDifference::no,
test::Common::no, test::CanCompare::yes, IsProxy>;
template <CanThrowAtCopyConstruction CanThrow>
using test_output = test::range<test::fwd, int_wrapper<CanThrow>, 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<CanThrowAtCopyConstruction::yes>::call<test_input<test::ProxyRef::no, CanThrowAtCopyConstruction::yes>,
test_output<CanThrowAtCopyConstruction::yes>>();
instantiator<CanThrowAtCopyConstruction::yes>::call<
test_input<test::ProxyRef::yes, CanThrowAtCopyConstruction::yes>,
test_output<CanThrowAtCopyConstruction::yes>>();
instantiator<CanThrowAtCopyConstruction::no>::call<test_input<test::ProxyRef::no, CanThrowAtCopyConstruction::no>,
test_output<CanThrowAtCopyConstruction::no>>();
instantiator<CanThrowAtCopyConstruction::no>::call<test_input<test::ProxyRef::yes, CanThrowAtCopyConstruction::no>,
test_output<CanThrowAtCopyConstruction::no>>();

throwing_test::call<test_input<test::ProxyRef::no, CanThrowAtCopyConstruction::yes>,
test_output<CanThrowAtCopyConstruction::yes>>();
throwing_test::call<test_input<test::ProxyRef::yes, CanThrowAtCopyConstruction::yes>,
test_output<CanThrowAtCopyConstruction::yes>>();
}
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
Loading