diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -2710,6 +2710,89 @@ }; #endif // >= C++20 && supports [[no_unique_address]] + +#if _LIBCPP_STD_VER > 17 +template>> +void __shared_ptr_destroy_elem(_Tp* __ptr) { __ptr->~_Tp(); } + +template +void __shared_ptr_destroy_elem(_Tp(*__ptr)[_Sz]) +{ + for (size_t __i = 0; __i < _Sz; ++__i) + __shared_ptr_destroy_elem(__ptr[__i]); +} + +template +struct __shared_ptr_array_info +{ + [[no_unique_address]] _Alloc __alloc; + size_t __size; +}; + +template +struct __shared_ptr_array + : __shared_weak_count +{ + using _Info = __shared_ptr_array_info<_Alloc>; + using _InfoBlock = typename std::aligned_storage::type; + using _DataBlock = typename std::aligned_storage::type; + + _LIBCPP_HIDE_FROM_ABI + _Tp* __get_elem_begin() _NOEXCEPT { return reinterpret_cast<_Tp*>(&__data); } + + _LIBCPP_HIDE_FROM_ABI + size_t __get_size() _NOEXCEPT { return reinterpret_cast<_Info*>(&__info)->__size; } + + _LIBCPP_HIDE_FROM_ABI + _Alloc* __get_alloc() _NOEXCEPT { + return _VSTD::addressof(reinterpret_cast<_Info*>(&__info)->__alloc); + } + + template + __shared_ptr_array(_Alloc const&__a, size_t __size, _Et const&__element) { + *reinterpret_cast<_Info*>(&__info) = {__a, __size}; + __fill_data(__element); + } + +private: + template + void __fill_data(_Et __element) { + _Tp *__ptr = __get_elem_begin(); + size_t __size = __get_size(); + for (size_t __i = 0; __i < __size; ++__i, ++__ptr) + ::new(static_cast(__ptr)) _Tp(__element); + } + + void __fill_data(false_type) { + _Tp *__ptr = __get_elem_begin(); + size_t __size = __get_size(); + for (size_t __i = 0; __i < __size; ++__i, ++__ptr) + ::new(static_cast(__ptr)) _Tp(); + } + + virtual void __on_zero_shared() _NOEXCEPT { + size_t __i = 0; + size_t __size = __get_size(); + _Tp *__ptr = __get_elem_begin(); + for (; __i < __size; ++__i, ++__ptr) { + __shared_ptr_destroy_elem(__ptr); + } + } + + virtual void __on_zero_shared_weak() _NOEXCEPT { + using _CharAlloc = typename __allocator_traits_rebind<_Alloc, char>::type; + _CharAlloc __tmp(*__get_alloc()); + __get_alloc()->~_Alloc(); + size_t __size = __get_size() * sizeof(_Tp) + sizeof(__shared_ptr_array); + allocator_traits<_CharAlloc>::deallocate(__tmp, reinterpret_cast(this), __size); + } + + _InfoBlock __info; + _DataBlock __data[0]; +}; + +#endif // _LIBCPP_STD_VER > 17 + struct __shared_ptr_dummy_rebind_allocator_type; template <> class _LIBCPP_TEMPLATE_VIS allocator<__shared_ptr_dummy_rebind_allocator_type> @@ -3412,13 +3495,80 @@ return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem(), _VSTD::addressof(*__control_block)); } -template::value> > +#if _LIBCPP_STD_VER > 17 + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a) +{ + using _ElemT = remove_extent_t<_Tp>; + using _ControlBlock = __shared_ptr_array<_ElemT, _Alloc>; + using _CharAllocator = typename __allocator_traits_rebind<_Alloc, char>::type; + + size_t __size = extent_v<_Tp> * sizeof(_ElemT) + sizeof(_ControlBlock); + __allocation_guard<_CharAllocator> __guard(__a, __size); + // false_type tells the constructor to defualt construct each element. + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, extent_v<_Tp>, false_type()); + auto __control_block = reinterpret_cast<_ControlBlock*>(__guard.__release_ptr()); + return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem_begin(), _VSTD::addressof(*__control_block)); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u) +{ + using _ElemT = remove_extent_t<_Tp>; + using _ControlBlock = __shared_ptr_array<_ElemT, _Alloc>; + using _CharAllocator = typename __allocator_traits_rebind<_Alloc, char>::type; + + size_t __size = extent_v<_Tp> * sizeof(_ElemT) + sizeof(_ControlBlock); + __allocation_guard<_CharAllocator> __guard(__a, __size); + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, extent_v<_Tp>, __u); + auto __control_block = reinterpret_cast<_ControlBlock*>(__guard.__release_ptr()); + return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem_begin(), _VSTD::addressof(*__control_block)); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) +{ + using _ElemT = remove_extent_t<_Tp>; + using _ControlBlock = __shared_ptr_array<_ElemT, _Alloc>; + using _CharAllocator = typename __allocator_traits_rebind<_Alloc, char>::type; + + size_t __size = __n * sizeof(_ElemT) + sizeof(_ControlBlock); + __allocation_guard<_CharAllocator> __guard(__a, __size); + // false_type tells the constructor to defualt construct each element. + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, __n, false_type()); + auto __control_block = reinterpret_cast<_ControlBlock*>(__guard.__release_ptr()); + return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem_begin(), _VSTD::addressof(*__control_block)); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) +{ + using _ElemT = remove_extent_t<_Tp>; + using _ControlBlock = __shared_ptr_array<_ElemT, _Alloc>; + using _CharAllocator = typename __allocator_traits_rebind<_Alloc, char>::type; + + size_t __size = __n * sizeof(_ElemT) + sizeof(_ControlBlock); + __allocation_guard<_CharAllocator> __guard(__a, __size); + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, __n, __u); + auto __control_block = reinterpret_cast<_ControlBlock*>(__guard.__release_ptr()); + return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem_begin(), _VSTD::addressof(*__control_block)); +} + +#endif // _LIBCPP_STD_VER > 17 + +template _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(_Args&& ...__args) { return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...); } + template inline _LIBCPP_INLINE_VISIBILITY bool diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/create_array.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/create_array.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/create_array.pass.cpp @@ -0,0 +1,204 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17 + +// + +// shared_ptr + +// template shared_ptr make_shared(size_t N); // T is U[] +// template +// shared_ptr allocate_shared(const A& a, size_t N); // T is U[] + +// template shared_ptr make_shared(); // T is U[N] +// template +// shared_ptr allocate_shared(const A& a); // T is U[N] + +// template +// shared_ptr make_shared(size_t N, const remove_extent_t& u); // T is U[] +// template +// shared_ptr allocate_shared(const A& a, size_t N, +// const remove_extent_t& u); // T is U[] + +// template shared_ptr +// make_shared(const remove_extent_t& u); // T is U[N] +// template +// shared_ptr allocate_shared(const A& a, +// const remove_extent_t& u); // T is U[N] + +#include +#include + +struct A { + static int count; + + A() { ++count; } + A(const A&) { ++count; } + ~A() { --count; } +}; + +int A::count = 0; + +struct B { + std::max_align_t val; +}; + +struct C { + static int count; + + int value; + C() : value(count++) {} +}; + +int C::count = 0; + +int main() { + { + using Unbound = A[]; + { + std::shared_ptr ptr0 = std::make_shared(8); + assert(A::count == 8); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8); + assert(A::count == 8); + } + assert(A::count == 0); + + using Unbound1 = A[][2]; + { + std::shared_ptr ptr0 = std::make_shared(8); + assert(A::count == 16); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8); + assert(A::count == 16); + } + assert(A::count == 0); + + using Unbound2 = A[][2][2]; + { + std::shared_ptr ptr0 = std::make_shared(8); + assert(A::count == 32); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8); + assert(A::count == 32); + } + assert(A::count == 0); + } + + { + using Bound = A[8]; + { + std::shared_ptr ptr0 = std::make_shared(); + assert(A::count == 8); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator()); + assert(A::count == 8); + } + assert(A::count == 0); + + using Bound1 = A[8][2]; + { + std::shared_ptr ptr0 = std::make_shared(); + assert(A::count == 16); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator()); + assert(A::count == 16); + } + assert(A::count == 0); + + using Bound2 = A[8][2][2]; + { + std::shared_ptr ptr0 = std::make_shared(); + assert(A::count == 32); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator()); + assert(A::count == 32); + } + assert(A::count == 0); + } + + { + using T = int[]; + std::shared_ptr ptr0 = std::make_shared(8, 42); + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8, 42); + + for (unsigned i = 0; i < 8; ++i) { + assert(ptr0[i] == 42); + assert(ptr1[i] == 42); + } + } + + { + using T = C[][2]; + std::shared_ptr ptr0 = std::make_shared(8); + C::count = 0; + std::shared_ptr ptr1 = std::allocate_shared(std::allocator(), 8); + C::count = 0; + + for (int i = 0; i < 8; ++i) { + assert(ptr0[i][0].value == i * 2); + assert(ptr1[i][0].value == i * 2); + assert(ptr0[i][1].value == (i * 2) + 1); + assert(ptr1[i][1].value == (i * 2) + 1); + } + } + + { + using T = int[8]; + std::shared_ptr ptr0 = std::make_shared(42); + std::shared_ptr ptr1 = std::allocate_shared(std::allocator(), 42); + + for (unsigned i = 0; i < 8; ++i) { + assert(ptr0[i] == 42); + assert(ptr1[i] == 42); + } + } + + { + using T = C[8][2]; + std::shared_ptr ptr0 = std::make_shared(); + C::count = 0; + std::shared_ptr ptr1 = std::allocate_shared(std::allocator()); + C::count = 0; + + for (int i = 0; i < 8; ++i) { + assert(ptr0[i][0].value == i * 2); + assert(ptr1[i][0].value == i * 2); + assert(ptr0[i][1].value == (i * 2) + 1); + assert(ptr1[i][1].value == (i * 2) + 1); + } + } + + // make sure alignment works + { + using T = B[8]; + + std::shared_ptr ptr = std::make_shared(); + assert(reinterpret_cast(ptr.get()) % alignof(B) == 0); + } +}