Skip to content

Commit

Permalink
Implement constexpr versions of the meow_memmove helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
miscco committed Oct 13, 2020
1 parent ae2dfb1 commit a3e3dc8
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 40 deletions.
16 changes: 0 additions & 16 deletions stl/inc/memory
Original file line number Diff line number Diff line change
Expand Up @@ -545,22 +545,7 @@ namespace ranges {
};

inline constexpr _Uninitialized_fill_n_fn uninitialized_fill_n{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts

// FUNCTION TEMPLATE construct_at
#if _HAS_CXX20
template <class _Ty, class... _Types>
_CONSTEXPR20_DYNALLOC auto construct_at(_Ty* const _Location, _Types&&... _Args) noexcept(
noexcept(::new (const_cast<void*>(static_cast<const volatile void*>(_Location)))
_Ty(_STD forward<_Types>(_Args)...))) // strengthened
-> decltype(
::new (const_cast<void*>(static_cast<const volatile void*>(_Location))) _Ty(_STD forward<_Types>(_Args)...)) {
return ::new (const_cast<void*>(static_cast<const volatile void*>(_Location))) _Ty(_STD forward<_Types>(_Args)...);
}

#ifdef __cpp_lib_concepts
namespace ranges {
// VARIABLE ranges::construct_at
class _Construct_at_fn : private _Not_quite_object {
public:
Expand All @@ -583,7 +568,6 @@ namespace ranges {
inline constexpr _Construct_at_fn construct_at{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX20

#if _HAS_CXX17
// FUNCTION TEMPLATE destroy_at
Expand Down
62 changes: 39 additions & 23 deletions stl/inc/xmemory
Original file line number Diff line number Diff line change
Expand Up @@ -1424,18 +1424,18 @@ struct _Uninitialized_backout { // struct to undo partially constructed ranges i
_Uninitialized_backout(const _Uninitialized_backout&) = delete;
_Uninitialized_backout& operator=(const _Uninitialized_backout&) = delete;

~_Uninitialized_backout() {
_CONSTEXPR20_DYNALLOC ~_Uninitialized_backout() {
_Destroy_range(_First, _Last);
}

template <class... _Types>
void _Emplace_back(_Types&&... _Vals) {
_CONSTEXPR20_DYNALLOC void _Emplace_back(_Types&&... _Vals) {
// construct a new element at *_Last and increment
_Construct_in_place(*_Last, _STD forward<_Types>(_Vals)...);
++_Last;
}

_NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last
constexpr _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last
_First = _Last;
return _Last;
}
Expand Down Expand Up @@ -1478,18 +1478,23 @@ namespace ranges {
// FUNCTION TEMPLATE _Uninitialized_move_unchecked
#if _HAS_IF_CONSTEXPR
template <class _InIt, class _NoThrowFwdIt>
_NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) {
_CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_move_unchecked(
_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) {
// move [_First, _Last) to raw [_Dest, ...)
if constexpr (_Memmove_in_uninitialized_move_is_safe<_InIt, _NoThrowFwdIt>) {
return _Memmove_forward(_First, _Last, _Dest);
} else {
_Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest};
for (; _First != _Last; ++_First) {
_Backout._Emplace_back(_STD move(*_First));
#ifdef __cpp_lib_is_constant_evaluated
if (!_STD is_constant_evaluated())
#endif // __cpp_lib_is_constant_evaluated
{
return _Memmove_forward(_First, _Last, _Dest);
}

return _Backout._Release();
}
_Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest};
for (; _First != _Last; ++_First) {
_Backout._Emplace_back(_STD move(*_First));
}

return _Backout._Release();
}
#else // ^^^ _HAS_IF_CONSTEXPR ^^^ // vvv !_HAS_IF_CONSTEXPR vvv
template <class _InIt, class _NoThrowFwdIt>
Expand Down Expand Up @@ -1617,25 +1622,36 @@ _Alloc_ptr_t<_Alloc> _Uninitialized_copy(

// FUNCTION TEMPLATE uninitialized_copy
#if _HAS_IF_CONSTEXPR
template <class _InIt, class _NoThrowFwdIt>
_CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_copy_unchecked(
_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) {
// copy [_First, _Last) to raw [_Dest, ...)
if constexpr (_Memmove_in_uninitialized_copy_is_safe<_InIt, _NoThrowFwdIt>) {
#ifdef __cpp_lib_is_constant_evaluated
if (!_STD is_constant_evaluated())
#endif // __cpp_lib_is_constant_evaluated
{
return _Memmove_forward(_First, _Last, _Dest);
}
}
_Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest};
for (; _First != _Last; ++_First) {
_Backout._Emplace_back(*_First);
}

_Dest = _Backout._Release();

return _Dest;
}

template <class _InIt, class _NoThrowFwdIt>
_NoThrowFwdIt uninitialized_copy(const _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) {
// copy [_First, _Last) to raw [_Dest, ...)
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_InIt>(_UFirst, _ULast));
if constexpr (_Memmove_in_uninitialized_copy_is_safe<decltype(_UFirst), decltype(_UDest)>) {
_UDest = _Memmove_forward(_UFirst, _ULast, _UDest);
} else {
_Uninitialized_backout<decltype(_UDest)> _Backout{_UDest};
for (; _UFirst != _ULast; ++_UFirst) {
_Backout._Emplace_back(*_UFirst);
}

_UDest = _Backout._Release();
}

_Seek_wrapped(_Dest, _UDest);
_Seek_wrapped(_Dest, _Uninitialized_copy_unchecked(_UFirst, _ULast, _UDest));
return _Dest;
}
#else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv
Expand Down
19 changes: 18 additions & 1 deletion stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,28 @@ struct _Get_rebind_alias<_Ty, _Other, void_t<typename _Ty::template rebind<_Othe
using type = typename _Ty::template rebind<_Other>;
};

// FUNCTION TEMPLATE construct_at
#if _HAS_CXX20
template <class _Ty, class... _Types>
_CONSTEXPR20_DYNALLOC auto construct_at(_Ty* const _Location, _Types&&... _Args) noexcept(
noexcept(::new (const_cast<void*>(static_cast<const volatile void*>(_Location)))
_Ty(_STD forward<_Types>(_Args)...))) // strengthened
-> decltype(
::new (const_cast<void*>(static_cast<const volatile void*>(_Location))) _Ty(_STD forward<_Types>(_Args)...)) {
return ::new (const_cast<void*>(static_cast<const volatile void*>(_Location))) _Ty(_STD forward<_Types>(_Args)...);
}
#endif // _HAS_CXX20

// FUNCTION TEMPLATE _Construct_in_place
template <class _Ty, class... _Types>
void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) {
_CONSTEXPR20_DYNALLOC void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(
is_nothrow_constructible_v<_Ty, _Types...>) {
#if _HAS_CXX20
_STD construct_at(_STD addressof(_Obj), _STD forward<_Types>(_Args)...);
#else // ^^^_HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv
::new (const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Obj))))
_Ty(_STD forward<_Types>(_Args)...);
#endif // !_HAS_CXX20
}

// STRUCT TEMPLATE pointer_traits
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ tests\P0718R2_atomic_smart_ptrs
tests\P0758R1_is_nothrow_convertible
tests\P0768R1_spaceship_operator
tests\P0769R2_shift_left_shift_right
tests\P0784R7_library_machinery
tests\P0784R7_library_support_for_more_constexpr_containers
tests\P0811R3_midpoint_lerp
tests\P0896R4_common_iterator
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0784R7_library_machinery/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 ..\usual_latest_matrix.lst
141 changes: 141 additions & 0 deletions tests/std/tests/P0784R7_library_machinery/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <assert.h>
#include <memory>

#pragma warning(disable : 4582) // '%s': constructor is not implicitly called
#pragma warning(disable : 4583) // '%s': destructor is not implicitly called

using namespace std;

struct int_wrapper_copy {
constexpr int_wrapper_copy() = default;
constexpr int_wrapper_copy(const int v) : _val(v){};

constexpr int_wrapper_copy(const int_wrapper_copy& other) : _val(other._val) {}
constexpr int_wrapper_copy& operator=(const int_wrapper_copy& other) {
_val = other._val;
return *this;
}

constexpr int_wrapper_copy(int_wrapper_copy&&) = delete;
constexpr int_wrapper_copy& operator=(int_wrapper_copy&&) = delete;

constexpr bool operator==(const int_wrapper_copy&) const = default;

int _val = 0;
};

struct int_wrapper_move {
constexpr int_wrapper_move() = default;
constexpr int_wrapper_move(const int v) : _val(v){};

constexpr int_wrapper_move(const int_wrapper_move&) = delete;
constexpr int_wrapper_move& operator=(const int_wrapper_move&) = delete;

constexpr int_wrapper_move(int_wrapper_move&& other) : _val(exchange(other._val, -1)) {}
constexpr int_wrapper_move& operator=(int_wrapper_move&& other) {
_val = exchange(other._val, -1);
return *this;
}

constexpr bool operator==(const int_wrapper_move&) const = default;

int _val = 0;
};

static constexpr int_wrapper_copy expected_copy[] = {1, 2, 3, 4};
static constexpr int_wrapper_move expected_move[] = {1, 2, 3, 4};
static constexpr int_wrapper_move expected_after_move[] = {-1, -1, -1, -1};

constexpr bool test() {
{ // _Copy_unchecked
int_wrapper_copy input[] = {1, 2, 3, 4};
int_wrapper_copy output[4] = {5, 6, 7, 8};

const same_as<int_wrapper_copy*> auto result = _Copy_unchecked(begin(input), end(input), begin(output));
assert(result == end(output));
assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output)));
}

{ // _Copy_backward_unchecked
int_wrapper_copy input[] = {1, 2, 3, 4};
int_wrapper_copy output[4] = {5, 6, 7, 8};

const same_as<int_wrapper_copy*> auto result = _Copy_backward_unchecked(begin(input), end(input), end(output));
assert(result == begin(output));
assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output)));
}

#if _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc)
{ // _Uninitialized_copy_unchecked
int_wrapper_copy input[] = {1, 2, 3, 4};
int_wrapper_copy output[4];

const same_as<int_wrapper_copy*> auto result =
_Uninitialized_copy_unchecked(begin(input), end(input), begin(output));
assert(result == end(output));
assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output)));
}
#endif // _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc)

{ // _Move_unchecked
int_wrapper_move input[] = {1, 2, 3, 4};
int_wrapper_move output[4] = {5, 6, 7, 8};

const same_as<int_wrapper_move*> auto result = _Move_unchecked(begin(input), end(input), begin(output));
assert(result == end(output));
assert(equal(begin(expected_move), end(expected_move), begin(output), end(output)));
if (is_constant_evaluated()) {
assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move)));
}
}

{ // _Move_backward_unchecked
int_wrapper_move input[] = {1, 2, 3, 4};
int_wrapper_move output[4] = {5, 6, 7, 8};

const same_as<int_wrapper_move*> auto result = _Move_backward_unchecked(begin(input), end(input), end(output));
assert(result == begin(output));
assert(equal(begin(expected_move), end(expected_move), begin(output), end(output)));
if (is_constant_evaluated()) {
assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move)));
}
}

{ // _Move_backward_common
int_wrapper_move input[] = {1, 2, 3, 4};
int_wrapper_move output[4] = {5, 6, 7, 8};

const same_as<int_wrapper_move*> auto result =
ranges::_Move_backward_common(begin(input), end(input), end(output));
assert(result == begin(output));
assert(equal(begin(expected_move), end(expected_move), begin(output), end(output)));
if (is_constant_evaluated()) {
assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move)));
}
}

#if _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc)
{ // _Uninitialized_move_unchecked
int_wrapper_move input[] = {1, 2, 3, 4};
int_wrapper_move output[4];

const same_as<int_wrapper_move*> auto result =
_Uninitialized_move_unchecked(begin(input), end(input), begin(output));
assert(result == end(output));
assert(equal(begin(expected_move), end(expected_move), begin(output), end(output)));
if (is_constant_evaluated()) {
assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move)));
}
}
#endif // _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc)
return true;
}

int main() {
test();
static_assert(test());
}

0 comments on commit a3e3dc8

Please sign in to comment.