Glen Fernandes is already working on a patch, so I decided to finish off my changes and throw them up on the Patch List so that Glen can access the tests parts that I had written (they are not incredibly extensive but do pass).
For a small implementation description: the code I use does the same in-place, fused-allocation technique used by other internal classes of shared_ptr, and also creates some helpers in a new bits/multi_dim.h header to simplify accessing multiple dimensions in a proper order (useful for destruction, but construction is easy enough). This might also come in handy down the road when mdspan (finally) gets standardized. This implementation does not update the internal __allocate_shared and __make_shared functions (I don't know why there seems to be a duplicate front-end for those functions: it seems a little weird to use both? Maybe it's for legacy reasons, albeit if that is the case then I don't need to update the internal versions and people should move to the non-internal version, yes?). 2020-01-02 JeanHeyd "ThePhD" Meneide <phdoftheho...@gmail.com> * include/bits/multi_dim.h (new): New helpers for multi dimensional access. * include/bits/alloc_traits.h: New helpers for multi dimensional array construction and destruction. * include/bits/allocated_ptr.h: New guard type for sized allocation. * include/bits/memoryfwd.h: std::align forward declaration. * include/bits/shared_ptr.h: Changes to support multi dimensional array access with optimized size/extent storage. * include/bits/stl_construct.h: Formatting. * testsuite/20_util/shared_ptr/creation.array_support.allocate.cc: New test. * testsuite/20_util/shared_ptr/creation.array_support.make.cc: New test.
diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h index 061d353e3f0..39dcb639143 100644 --- a/libstdc++-v3/include/bits/alloc_traits.h +++ b/libstdc++-v3/include/bits/alloc_traits.h @@ -37,6 +37,9 @@ # include <bits/ptr_traits.h> # include <ext/numeric_traits.h> #endif +#if __cplusplus > 201703L +#include <bits/multi_dim.h> +#endif // C++20 and above multi dimensional tools namespace std _GLIBCXX_VISIBILITY(default) { @@ -703,6 +706,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Alloc> using _RequireNotAllocator = typename enable_if<!__is_allocator<_Alloc>::value, _Alloc>::type; + + template <typename _Alloc> + using __alloc_size + = typename allocator_traits<_Alloc>::size_type; #endif // C++11 /** @@ -733,6 +740,189 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Destroy(__first, __last); } + + +#if __cplusplus > 201703L + /// Helpers for getting multi-dimensional construction an destruction + /// done properly. Obeys C++ Standard rules for array acccess: works + /// in constexpr. + + template<typename _ElemAlloc, typename _Ty, typename _Init> + constexpr void + __recursive_element_construct_1 (_ElemAlloc& __elem_alloc, + _Ty* __ptr, + __alloc_size<_ElemAlloc>& __constructed, + __alloc_size<_ElemAlloc> __extent, + const _Init& __value) + { + using _SizeType = __alloc_size<_ElemAlloc>; + if constexpr (rank_v<_Ty> == 0) + { + for (_SizeType __idx = 0; __idx < __extent; ++__idx) + { + _Ty* __element_ptr = __ptr + __idx; + allocator_traits<_ElemAlloc>::construct(__elem_alloc, + __element_ptr, + __value[__idx]); + ++__constructed; + } + } + else + { + using _Tl = std::remove_extent_t<_Ty>; + for (_SizeType __idx = 0; __idx < __extent; ++__idx) + { + _Tl* __elem_ptr = __ptr[__idx]; + __recursive_element_construct_1(__elem_alloc, + __elem_ptr, + __constructed, + extent_v<_Ty>, + __value[__idx]); + } + } + } + + template<typename _Ty, typename _ElemAlloc, typename _Init> + constexpr void + __recursive_element_construct (_ElemAlloc& __elem_alloc, _Ty* __ptr, + __alloc_size<_ElemAlloc>& __constructed, + __alloc_size<_ElemAlloc> __extent, + const _Init& __value) + { + using _SizeType = __alloc_size<_ElemAlloc>; + if constexpr (rank_v<_Ty> == 0) + { + for (_SizeType __idx = 0; __idx < __extent; ++__idx) + { + _Ty* __elem_ptr = __ptr + __idx; + allocator_traits<_ElemAlloc>::construct(__elem_alloc, + __elem_ptr, + __value); + ++__constructed; + } + } + else + { + using _Tl = std::remove_extent_t<_Ty>; + for (_SizeType __idx = 0; __idx < __extent; ++__idx) + { + _Tl* __elem_ptr = __ptr[__idx]; + __recursive_element_construct_1(__elem_alloc, + __elem_ptr, + __constructed, + extent_v<_Ty>, + __value); + } + } + } + + template<typename _Ty, typename _Alloc, typename... _Args> + constexpr void + __unbounded_array_construct (_Alloc& __alloc, _Ty* __ptr, + __alloc_size<_Alloc> __extent, + __alloc_size<_Alloc>& __constructed, + _Args&&... __args) + { + static_assert((sizeof...(_Args) == 0) || (sizeof...(_Args) == 1), + "must either be value-initialized or copy-initialized"); + if constexpr (sizeof...(_Args) == 0) + { + for (__alloc_size<_Alloc> __idx = 0; __idx < __extent; ++__idx) + { + _Ty* __elem_ptr = __ptr + __idx; + allocator_traits<_Alloc>::construct(__alloc, __elem_ptr); + __constructed += sizeof(_Ty) / sizeof(remove_all_extents_t<_Ty>); + } + } + else + { + using _ElemAlloc = __alloc_rebind<_Alloc, remove_all_extents_t<_Ty>>; + _ElemAlloc __elem_alloc{__alloc}; + const _Ty& __value{forward<_Args>(__args)...}; + __recursive_element_construct(__elem_alloc, __ptr, __constructed, + __extent, + __value); + } + } + + template<typename _Ty, typename _Alloc, typename... _Args> + constexpr void + __array_construct (_Alloc& __alloc, _Ty& __arr, + __alloc_size<_Alloc>& __constructed, + _Args&&... __args) + { + __unbounded_array_construct(__alloc, __arr, extent_v<_Ty>, __constructed, forward<_Args>(__args)...); + } + + template<typename _ElemAlloc, typename _Ty, typename _Access> + constexpr void __array_destroy_element_at (_ElemAlloc& __elem_alloc, _Ty* __ptr, + const _Access& __access, + __alloc_size<_ElemAlloc> __access_idx) noexcept + { + constexpr size_t _Rank = rank_v<_Ty>; + if constexpr (_Rank == 0) + { + _Ty* __elem_ptr = __ptr + __access[__access_idx]; + allocator_traits<_ElemAlloc>::destroy(__elem_alloc, __elem_ptr); + } + else + { + using _Tl = remove_extent_t<_Ty>; + _Tl* __elem_ptr = __ptr[__access[__access_idx]]; + __array_destroy_element_at(__elem_alloc, + __elem_ptr, + __access, + __access_idx + 1); + } + } + + template<typename _Ty, typename _Alloc> + constexpr void __unbounded_array_destroy (_Alloc& __alloc, _Ty* __arr, + __alloc_size<_Alloc> __extent, + __alloc_size<_Alloc> __count) noexcept + { + if (__count == 0) + return; + + using _SizeType = __alloc_size<_Alloc>; + constexpr size_t __total_sub_extents = __total_extent_v<_Ty>; + _SizeType __max_elements = (__total_sub_extents == 0 + ? 1 + : __total_sub_extents) + * __extent; + if (__max_elements == __count) + { + // if possible, shortcut the shenanigans + for (_SizeType __idx = __extent; __idx-- > 0; --__idx) + { + _Ty* __elem_ptr = __arr + __idx; + allocator_traits<_Alloc>::destroy(__alloc, __elem_ptr); + } + + return; + } + + using _ElemAlloc = __alloc_rebind<_Alloc, remove_all_extents_t<_Ty>>; + constexpr size_t _Rank = 1 + rank_v<_Ty>; + const auto __extents = __extents_of<_Ty>(__extent); + const auto __strides = __strides_of<_Rank>(__extents); + _ElemAlloc __elem_alloc{__alloc}; + for (_SizeType __idx = __count; __idx-- > 0 ;) + { + auto __multi_idx = __index_to_multi_index<_Rank>(__idx, __strides); + __array_destroy_element_at(__elem_alloc, __arr, __multi_idx, 0); + } + } + + template<typename _Ty, typename _Alloc> + constexpr void __array_destroy (_Alloc& __alloc, _Ty& __arr, + __alloc_size<_Alloc> __count) noexcept + { + return __unbounded_array_destroy(__alloc, __arr, + extent_v<_Ty>, __count); + } +#endif // C++20 array allocator construction helpers + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // _ALLOC_TRAITS_H diff --git a/libstdc++-v3/include/bits/allocated_ptr.h b/libstdc++-v3/include/bits/allocated_ptr.h index 5058ab08466..84857185924 100644 --- a/libstdc++-v3/include/bits/allocated_ptr.h +++ b/libstdc++-v3/include/bits/allocated_ptr.h @@ -97,6 +97,77 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return { __a, std::allocator_traits<_Alloc>::allocate(__a, 1) }; } +#if __cplusplus > 201703L + + template <typename _Alloc> + using __alloc_size_t = + typename ::std::allocator_traits<_Alloc>::size_type; + + /// Non-standard RAII type for managing sized pointers + /// obtained from allocators with additional information. + template<typename _Alloc> + struct __sized_allocated_ptr + { + using pointer = typename allocator_traits<_Alloc>::pointer; + using value_type = typename allocator_traits<_Alloc>::value_type; + using size_type = __alloc_size_t<_Alloc>; + + /// Take ownership of __ptr + __sized_allocated_ptr(_Alloc& __a, size_type __ptr_size, + pointer __ptr) noexcept + : _M_alloc(std::__addressof(__a)), _M_ptr(__ptr), _M_size(__ptr_size) + { } + + /// Convert __ptr to allocator's pointer type and take ownership of it + template<typename _Ptr, + typename _Req = _Require<is_same<_Ptr, value_type*>>> + __sized_allocated_ptr(_Alloc& __a, size_type __ptr_size, + _Ptr __ptr) noexcept + : _M_alloc(std::__addressof(__a)), + _M_ptr(pointer_traits<pointer>::pointer_to(*__ptr)), + _M_size(__ptr_size) + { } + + /// Transfer ownership of the owned pointer + __sized_allocated_ptr(__sized_allocated_ptr&& __gd) noexcept + : _M_alloc(__gd._M_alloc), _M_ptr(__gd._M_ptr), _M_size(__gd._M_size) + { __gd._M_ptr = nullptr; } + + /// Deallocate the owned pointer + ~__sized_allocated_ptr() + { + if (_M_ptr != nullptr) + std::allocator_traits<_Alloc>::deallocate(*_M_alloc, _M_ptr, _M_size); + } + + /// Release ownership of the owned pointer + __sized_allocated_ptr& + operator=(std::nullptr_t) noexcept + { + _M_ptr = nullptr; + return *this; + } + + /// Get the address that the owned pointer refers to. + value_type* get() { return std::__to_address(_M_ptr); } + + private: + _Alloc* _M_alloc; + pointer _M_ptr; + size_type _M_size; + }; + + /// Allocate space for maybe multiple objects using __a + template<typename _Alloc> + __sized_allocated_ptr<_Alloc> + __allocate_guarded_size(_Alloc& __a, + typename ::std::allocator_traits<_Alloc>::size_type __ptr_size) + { + return { __a, __ptr_size, + std::allocator_traits<_Alloc>::allocate(__a, __ptr_size) }; + } +#endif // C++20 and above unbounded array sized allocate support + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/bits/memoryfwd.h b/libstdc++-v3/include/bits/memoryfwd.h index af1a1c69c64..4d246fd6123 100644 --- a/libstdc++-v3/include/bits/memoryfwd.h +++ b/libstdc++-v3/include/bits/memoryfwd.h @@ -72,6 +72,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Declare uses_allocator so it can be specialized in \<queue\> etc. template<typename, typename> struct uses_allocator; + + /// Declare align so it can be used in shared_ptr_base + void* + align(size_t, size_t, void*&, size_t&) noexcept; #endif /// @} group memory diff --git a/libstdc++-v3/include/bits/multi_dim.h b/libstdc++-v3/include/bits/multi_dim.h new file mode 100644 index 00000000000..a9189f9e0e9 --- /dev/null +++ b/libstdc++-v3/include/bits/multi_dim.h @@ -0,0 +1,117 @@ +// Multi dimensional tools and helpers -*- C++ -*- + +// Copyright (C) 2011-2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +/** @file bits/mutli_dim.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{memory} + */ + +#ifndef _MULTI_DIM_H +#define _MULTI_DIM_H 1 + +#if __cplusplus > 201703L + +#include <array> +#include <type_traits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<typename _Ty> + struct __total_extent : extent<_Ty> {}; + + template<typename _Ty, size_t _Extent> + struct __total_extent<_Ty[_Extent]> + : ::std::integral_constant<size_t, extent<_Ty>::value == 0 + ? _Extent + : __total_extent<_Ty>::value * _Extent> {}; + + template<typename _Ty> + // internal: total_extent helper + constexpr inline size_t __total_extent_v = __total_extent<_Ty>::value; + + template <typename _Ty, typename _SizeType = size_t, size_t... _Idx, typename... _Args> + constexpr std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>> + __extents_of (index_sequence<_Idx...>, _Args&&... __args) noexcept + { + std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>> __extents{ + std::forward<_Args>(__args)..., extent_v<_Ty, _Idx>... + }; + return __extents; + } + + template <typename _Ty, typename _SizeType = size_t, typename... _Args> + constexpr std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>> + __extents_of (_Args&&... __args) noexcept + { + return __extents_of<_Ty>(make_index_sequence<rank_v<_Ty>>(), + std::forward<_Args>(__args)...); + } + + template <typename _Ty, typename _SizeType = size_t, typename _Extents> + constexpr array<_SizeType, rank_v<_Ty>> + __strides_of (const _Extents& __extents) noexcept + { + array<_SizeType, rank_v<_Ty>> __strides{}; + _SizeType __rank_stride = 1; + for (size_t __rank_idx = rank_v<_Ty>; __rank_idx-- > 0;) { + __strides[__rank_idx] = __rank_stride; + __rank_stride *= __extents[__rank_idx]; + } + return __strides; + } + + template <size_t _Rank, typename _SizeType = size_t, typename _Extents> + constexpr array<_SizeType, _Rank> + __strides_of (const _Extents& __extents) noexcept + { + array<_SizeType, _Rank> __strides{}; + _SizeType __rank_stride = 1; + for (size_t __rank_idx = _Rank; __rank_idx-- > 0;) { + __strides[__rank_idx] = __rank_stride; + __rank_stride *= __extents[__rank_idx]; + } + return __strides; + } + + template <size_t _Rank, typename _SizeType, typename _Strides> + constexpr std::array<_SizeType, _Rank> + __index_to_multi_index(_SizeType __idx, const _Strides& __strides) noexcept + { + std::array<_SizeType, _Rank> __multi_index{}; + for (size_t __rank_index = 0; __rank_index < _Rank; ++__rank_index) { + _SizeType __stride = __strides[__rank_index]; + __multi_index[__rank_index] = __idx / __stride; + __idx -= __multi_index[__rank_index] * __stride; + } + return __multi_index; + } + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace + +#endif // C++20 or above + +#endif /* _MULTI_DIM_H */ diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h index c4df3582e20..2d1a8a2bba3 100644 --- a/libstdc++-v3/include/bits/shared_ptr.h +++ b/libstdc++-v3/include/bits/shared_ptr.h @@ -99,6 +99,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } + namespace __detail + { + // friendship cooperation due to C++20 because constraints + // do not handle template function friending well at all + struct __shared_friend; + } + + /** * @brief A smart pointer with reference-counted copy semantics. * @@ -408,9 +416,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...) { } - template<typename _Yp, typename _Alloc, typename... _Args> - friend shared_ptr<_Yp> - allocate_shared(const _Alloc& __a, _Args&&... __args); +#if __cplusplus > 201703L + // This constructor is non-standard, it is used by allocate_shared. + template<typename _Alloc, typename... _Args> + shared_ptr(in_place_type_t<_Tp> __type_tag, + _Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args) + : __shared_ptr<_Tp>(__type_tag, __tag, std::forward<_Args>(__args)...) + { } +#endif // C++20 support for proper array make_shared + + friend struct __detail::__shared_friend; // This constructor is non-standard, it is used by weak_ptr::lock(). shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t) @@ -419,6 +434,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend class weak_ptr<_Tp>; }; + namespace __detail + { + struct __shared_friend + { + template <typename _Tp, typename _Alloc, typename... _Args> + static shared_ptr<_Tp> + __allocate_shared (_Sp_alloc_shared_tag<_Alloc> __tag, + _Args&&... __args) + { +#if __cplusplus <= 201703L + return shared_ptr<_Tp> (__tag, ::std::forward<_Args>(__args)...); +#else + return shared_ptr<_Tp> (in_place_type<_Tp>, __tag, + ::std::forward<_Args>(__args)...); +#endif + } + }; + } + #if __cpp_deduction_guides >= 201606 template<typename _Tp> shared_ptr(weak_ptr<_Tp>) -> shared_ptr<_Tp>; @@ -825,6 +859,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @relates shared_ptr @{ +#if __cplusplus <= 201703L + /** * @brief Create an object that is owned by a shared_ptr. * @param __a An allocator. @@ -838,10 +874,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION */ template<typename _Tp, typename _Alloc, typename... _Args> inline shared_ptr<_Tp> - allocate_shared(const _Alloc& __a, _Args&&... __args) + allocate_shared (const _Alloc& __a, _Args&&... __args) { - return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a}, - std::forward<_Args>(__args)...); + return __detail::__shared_friend::__allocate_shared<_Tp> ( + _Sp_alloc_shared_tag<_Alloc>{__a}, std::forward<_Args>(__args)...); } /** @@ -860,6 +896,190 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::forward<_Args>(__args)...); } +#else + + /** + * @brief Create an object that is owned by a shared_ptr. + * @param __a An allocator. + * @param __args Arguments for the @a _Tp object's constructor. + * @return A shared_ptr that owns the newly created object. + * @throw An exception thrown from @a _Alloc::allocate or from the + * constructor of @a _Tp. + * + * A copy of @a __a will be used to allocate memory for the shared_ptr + * and the new object. + */ + template<typename _Tp, typename _Alloc, typename... _Args> + requires (!is_array_v<_Tp>) + inline shared_ptr<_Tp> + allocate_shared (const _Alloc& __a, _Args&&... __args) + { + return __detail::__shared_friend::__allocate_shared<_Tp> ( + _Sp_alloc_shared_tag<_Alloc>{__a}, + std::forward<_Args>(__args)...); + } + + /** + * @brief Create a bounded array object that is owned by a shared_ptr, + * with all elements given an initial value of __value. + * @param __value Initial value to construct all elements with. + * @return A shared_ptr that owns the newly created bounded array object. + * @throw std::bad_alloc, or an exception thrown from the + * copy constructor of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp, typename _Alloc> + requires (is_bounded_array_v<_Tp>) + inline shared_ptr<_Tp> + allocate_shared (const _Alloc& __a, const remove_extent_t<_Tp>& __value) + { + return __detail::__shared_friend::__allocate_shared<_Tp> ( + _Sp_alloc_shared_tag<_Alloc>{__a}, + __value); + } + + /** + * @brief Create a bounded array object that is owned by a shared_ptr, + * with the elements value initialized. + * @return A shared_ptr that owns the newly created bounded array object. + * @throw std::bad_alloc, or an exception thrown from the + * value initialization of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp, typename _Alloc> + requires (is_bounded_array_v<_Tp>) + inline shared_ptr<_Tp> + allocate_shared (const _Alloc& __a) + { + return __detail::__shared_friend::__allocate_shared<_Tp> ( + _Sp_alloc_shared_tag<_Alloc>{__a}); + } + + /** + * @brief Create an array object that is owned by a shared_ptr, + * with copy constructed elements. + * @param __value Initial value to construct all elements with. + * @param __num_elements The number of elements to create the array with. + * @return A shared_ptr that owns the newly created bounded array object. + * @throw std::bad_alloc, or an exception thrown from the + * copy constructor of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp, typename _Alloc> + requires (is_unbounded_array_v<_Tp>) + inline shared_ptr<_Tp> + allocate_shared (const _Alloc& __a, + __alloc_size_t<_Alloc> __num_elements, + const remove_extent_t<_Tp>& __value) + { + return __detail::__shared_friend::__allocate_shared<_Tp> ( + _Sp_alloc_shared_tag<_Alloc>{__a}, __num_elements, __value); + } + + /** + * @brief Create an array object that is owned by a shared_ptr, + * with value-initialized elements. + * @param __num_elements The number of elements to create the array with. + * @return A shared_ptr that owns the newly created array object of size __num_elements. + * @throw std::bad_alloc, or an exception thrown from the + * value initialization of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp, typename _Alloc> + requires (is_unbounded_array_v<_Tp>) + inline shared_ptr<_Tp> + allocate_shared (const _Alloc& __a, + __alloc_size_t<_Alloc> __num_elements) + { + return __detail::__shared_friend::__allocate_shared<_Tp> ( + _Sp_alloc_shared_tag<_Alloc>{__a}, __num_elements); + } + + /** + * @brief Create an object that is owned by a shared_ptr. + * @param __args Arguments for the @a _Tp object's constructor. + * @return A shared_ptr that owns the newly created object. + * @throw std::bad_alloc, or an exception thrown from the + * constructor of @a _Tp. + */ + template<typename _Tp, typename... _Args> + requires (!is_array_v<_Tp>) + inline shared_ptr<_Tp> + make_shared(_Args&&... __args) + { + using _Tp_nc = remove_cv_t<_Tp>; + return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), + std::forward<_Args>(__args)...); + } + + /** + * @brief Create a bounded array object that is owned by a shared_ptr, + * with all elements given an initial value of __value. + * @param __value Initial value to construct all elements with. + * @return A shared_ptr that owns the newly created bounded array object. + * @throw std::bad_alloc, or an exception thrown from the + * copy constructor of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp> + requires (is_bounded_array_v<_Tp>) + inline shared_ptr<_Tp> + make_shared(const remove_extent_t<_Tp>& __value) + { + using _Tp_nc = remove_cv_t<_Tp>; + return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), __value); + } + + /** + * @brief Create a bounded array object that is owned by a shared_ptr, + * with the elements value initialized. + * @return A shared_ptr that owns the newly created bounded array object. + * @throw std::bad_alloc, or an exception thrown from the + * value initialization of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp> + requires (is_bounded_array_v<_Tp>) + inline shared_ptr<_Tp> + make_shared() + { + using _Tp_nc = remove_cv_t<_Tp>; + return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>()); + } + + /** + * @brief Create an array object that is owned by a shared_ptr, + * with copy constructed elements. + * @param __value Initial value to construct all elements with. + * @param __num_elements The number of elements to create the array with. + * @return A shared_ptr that owns the newly created bounded array object. + * @throw std::bad_alloc, or an exception thrown from the + * copy constructor of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp> + requires (is_unbounded_array_v<_Tp>) + inline shared_ptr<_Tp> + make_shared(size_t __num_elements, const remove_extent_t<_Tp>& __value) + { + using _Tp_nc = remove_cv_t<remove_extent_t<_Tp>>; + return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), + __num_elements, __value); + } + + /** + * @brief Create an array object that is owned by a shared_ptr, + * with value-initialized elements. + * @param __num_elements The number of elements to create the array with. + * @return A shared_ptr that owns the newly created array object of size __num_elements. + * @throw std::bad_alloc, or an exception thrown from the + * value initialization of @a std::remove_extent_t<_Tp>. + */ + template<typename _Tp> + requires (is_unbounded_array_v<_Tp>) + inline shared_ptr<_Tp> + make_shared(size_t __num_elements) + { + using _Tp_nc = remove_cv_t<remove_extent_t<_Tp>>; + return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), + __num_elements); + } + +#endif // C++17 and below | C++20 and above make/allocate_shared + /// std::hash specialization for shared_ptr. template<typename _Tp> struct hash<shared_ptr<_Tp>> diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h index c9017ede4cb..e0b2e5774ab 100644 --- a/libstdc++-v3/include/bits/shared_ptr_base.h +++ b/libstdc++-v3/include/bits/shared_ptr_base.h @@ -54,6 +54,9 @@ #include <bits/refwrap.h> #include <bits/stl_function.h> #include <ext/aligned_buffer.h> +#if __cplusplus > 201703L +#include <utility> +#endif // C++20 and above make/allocate_shared array support namespace std _GLIBCXX_VISIBILITY(default) { @@ -89,6 +92,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using __gnu_cxx::_S_mutex; using __gnu_cxx::_S_atomic; + class _Zero + { }; + // Empty helper class except when the template argument is _S_mutex. template<_Lock_policy _Lp> class _Mutex_base @@ -363,6 +369,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION class __shared_count; + template <typename _Tp> + using __Sp_needs_alloc_size = + integral_constant<bool, is_array<_Tp>::value + && (extent<_Tp>::value == 0)>; + + template<typename _SizeType, size_t _Extent> + class _Sp_alloc_size + { + public: + _Sp_alloc_size(_SizeType __size) noexcept + { __glibcxx_assert(__size == _Extent); } + + static _SizeType + _M_alloc_size () noexcept + { return _Extent; } + }; + + template<typename _SizeType> + class _Sp_alloc_size<_SizeType, 0> + { + public: + _Sp_alloc_size(_SizeType __size) noexcept + : _M_size(__size) + { } + + _SizeType + _M_alloc_size() const noexcept + { return _M_size; } + + _SizeType _M_size; + }; + // Counted ptr with no deleter or allocator support template<typename _Ptr, _Lock_policy _Lp> class _Sp_counted_ptr final : public _Sp_counted_base<_Lp> @@ -416,6 +454,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static _Tp& _S_get(_Sp_ebo_helper& __eboh) { return static_cast<_Tp&>(__eboh); } + + static const _Tp& + _S_get(const _Sp_ebo_helper& __eboh) + { return static_cast<const _Tp&>(__eboh); } }; /// Specialization not using EBO. @@ -429,6 +471,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _S_get(_Sp_ebo_helper& __eboh) { return __eboh._M_tp; } + static const _Tp& + _S_get(const _Sp_ebo_helper& __eboh) + { return __eboh._M_tp; } + private: _Tp _M_tp; }; @@ -437,18 +483,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp> class _Sp_counted_deleter final : public _Sp_counted_base<_Lp> { - class _Impl : _Sp_ebo_helper<0, _Deleter>, _Sp_ebo_helper<1, _Alloc> + using _Del_base = _Sp_ebo_helper<0, _Deleter>; + using _Alloc_base = _Sp_ebo_helper<1, _Alloc>; + + class _Impl : _Del_base, _Alloc_base { - typedef _Sp_ebo_helper<0, _Deleter> _Del_base; - typedef _Sp_ebo_helper<1, _Alloc> _Alloc_base; - public: _Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept : _M_ptr(__p), _Del_base(std::move(__d)), _Alloc_base(__a) { } - _Deleter& _M_del() noexcept { return _Del_base::_S_get(*this); } - _Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); } + _Deleter& _M_del() noexcept + { return _Del_base::_S_get(*this); } + + _Alloc& _M_alloc() noexcept + { return _Alloc_base::_S_get(*this); } _Ptr _M_ptr; }; @@ -468,7 +517,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION virtual void _M_dispose() noexcept - { _M_impl._M_del()(_M_impl._M_ptr); } + { + _M_impl._M_del()(_M_impl._M_ptr); + } virtual void _M_destroy() noexcept @@ -503,6 +554,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: template<typename _Tp, typename _Alloc, _Lock_policy _Lp> friend class _Sp_counted_ptr_inplace; + template<typename _Tp, typename _Alloc, size_t _Extent, _Lock_policy _Lp> + friend class _Sp_counted_sized_ptr_inplace; static const type_info& _S_ti() noexcept _GLIBCXX_VISIBILITY(default) @@ -523,14 +576,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp, typename _Alloc, _Lock_policy _Lp> class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp> { - class _Impl : _Sp_ebo_helper<0, _Alloc> - { - typedef _Sp_ebo_helper<0, _Alloc> _A_base; + using _Alloc_base = _Sp_ebo_helper<0, _Alloc>; + class _Impl : _Alloc_base + { public: - explicit _Impl(_Alloc __a) noexcept : _A_base(__a) { } + explicit _Impl(_Alloc __a) noexcept + : _Alloc_base(__a) + { } - _Alloc& _M_alloc() noexcept { return _A_base::_S_get(*this); } + _Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); } __gnu_cxx::__aligned_buffer<_Tp> _M_storage; }; @@ -596,6 +651,179 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Impl _M_impl; }; +#if __cplusplus > 201703L + template<typename _Ty, typename _Alloc, size_t _Extent, _Lock_policy _Lp> + class _Sp_counted_sized_ptr_inplace final : public _Sp_counted_base<_Lp> + { + // it is the user's job to track + // allocation size and deletion size + // when they create something manually with an allocator + // and/or a deleter and pass it directly + // to std::shared_ptr's constructor, + // that's why this machinery is present nowhere else + // but here. Originally, we attempted to modify + // _Sp_counted_ptr_inplace, + // but function changes that relied on data members + // -- even ones that can be optimized out -- can + // affect ABI and thus blow up code. + // Therefore, we needed a new derived + // implementation. + using _Tp = conditional_t<_Extent == 0 + , _Ty + , remove_extent_t<_Ty>>; + using _SizeType = __alloc_size_t<_Alloc>; + using _AllocSize = _Sp_alloc_size<_SizeType, _Extent>; + using _Alloc_base = _Sp_ebo_helper<0, _Alloc>; + using _Alloc_size_base = _Sp_ebo_helper<1, _AllocSize>; + using _StorageType = conditional_t<_Extent == 0 + , _Zero + , __gnu_cxx::__aligned_buffer<_Ty>>; + + alignas(_Ty) class _Impl : _Alloc_base, _Alloc_size_base + { + public: + explicit _Impl(_Alloc __a, _SizeType __size) noexcept + : _Alloc_base(__a), _Alloc_size_base(__size) + { } + + _Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); } + + _SizeType + _M_alloc_size() const noexcept + { return _Alloc_size_base::_S_get(*this)._M_alloc_size(); } + + _SizeType + _M_Impl_size() const noexcept + { return this->_S_Impl_size(this->_M_alloc_size()); } + + static _SizeType + _S_Impl_size (_SizeType __num_elements) noexcept + { + if constexpr (_Extent == 0) + { + _SizeType __division_step = sizeof(_Ty) - 1; + _SizeType __offset + = static_cast<_SizeType>(sizeof(_Sp_counted_sized_ptr_inplace)); + return ((__offset + __division_step) / sizeof(_Ty)) + + __num_elements; + } + else + { + return 1; + } + } + + [[no_unique_address]] _StorageType _M_storage; + }; + + public: + using __allocator_type + = conditional_t<_Extent == 0 + , _Alloc + , __alloc_rebind<_Alloc, _Sp_counted_sized_ptr_inplace>>; + + // Alloc parameter is not a reference so doesn't alias anything in __args + // On failure (exception), destroy + rethrow + template<typename... _Args> + _Sp_counted_sized_ptr_inplace(_Alloc __a, _SizeType __num_elements, + _Args&&... __args) + : _M_impl(__a, __num_elements) + { + // constructors might throw... + // FIXME: use destructor to unwind construct, + // rather than __catch + // (much faster codegen on many compilers) + _SizeType __num{}; + __try + { + __unbounded_array_construct (__a, this->_M_ptr(), + this->_M_impl._M_alloc_size(), + __num, + std::forward<_Args>(__args)...); + __glibcxx_assert(__num == this->_M_impl._M_alloc_size()); + } + __catch(...) + { + __unbounded_array_destroy (__a, this->_M_ptr(), + this->_M_impl._M_alloc_size(), __num); + __throw_exception_again; + } + } + + ~_Sp_counted_sized_ptr_inplace() noexcept { } + + virtual void + _M_dispose() noexcept + { + __unbounded_array_destroy (this->_M_impl._M_alloc(), this->_M_ptr(), + this->_M_impl._M_alloc_size(), + this->_M_impl._M_alloc_size()); + } + + // Override because the allocator needs to know the dynamic type + virtual void + _M_destroy() noexcept + { + using _Pointer = typename allocator_traits<__allocator_type>::pointer; + __allocator_type __al(this->_M_impl._M_alloc()); + __sized_allocated_ptr<__allocator_type> __guard_ptr{ __al, + this->_M_impl._M_Impl_size(), reinterpret_cast<_Pointer>(this) }; + this->~_Sp_counted_sized_ptr_inplace(); + } + + private: + friend class __shared_count<_Lp>; // To be able to call _M_ptr(). + + // No longer used, but code compiled against old libstdc++ headers + // might still call it from __shared_ptr ctor to get the pointer out. + virtual void* + _M_get_deleter(const std::type_info& __ti) noexcept override + { + auto __ptr = const_cast<typename remove_cv<_Tp>::type*>(_M_ptr()); + // Check for the fake type_info first, so we don't try to access it + // as a real type_info object. Otherwise, check if it's the real + // type_info for this class. With RTTI enabled we can check directly, + // or call a library function to do it. + if (&__ti == &_Sp_make_shared_tag::_S_ti() + || +#if __cpp_rtti + __ti == typeid(_Sp_make_shared_tag) +#else + _Sp_make_shared_tag::_S_eq(__ti) +#endif + ) + return __ptr; + return nullptr; + } + + _Tp* + _M_ptr() noexcept + { + if constexpr (_Extent == 0) + { + void* __ptr = static_cast<void*>(this + 1); + size_t __space = this->_M_impl._M_alloc_size() + * sizeof(_Ty); + void* __adjusted + = std::align(alignof(_Ty), __space, __ptr, __space); + // if this triggers then alignof() and friends failed us + // at a language level + __glibcxx_assert(__adjusted != nullptr); + return reinterpret_cast<_Tp*>(__adjusted); + } + else + return this->_M_impl._M_storage._M_ptr()[0]; + } + + static _SizeType + _S_Impl_size (_SizeType __num_elements) noexcept + { return _Impl::_S_Impl_size(__num_elements); } + + _Impl _M_impl; + }; + +#endif // C++20 and above make/allocate_shared array support + // The default deleter for shared_ptr<T[]> and shared_ptr<T[N]>. struct __sp_array_delete { @@ -672,16 +900,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __shared_count(_Tp*& __p, _Sp_alloc_shared_tag<_Alloc> __a, _Args&&... __args) { - typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type; - typename _Sp_cp_type::__allocator_type __a2(__a._M_a); - auto __guard = std::__allocate_guarded(__a2); - _Sp_cp_type* __mem = __guard.get(); - auto __pi = ::new (__mem) - _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...); - __guard = nullptr; - _M_pi = __pi; - __p = __pi->_M_ptr(); + this->__single_allocate(__p, __a, std::forward<_Args>(__args)...); + } + +#if __cplusplus > 201703L + template<typename _Real, typename _Tp, typename _Alloc, typename... _Args> + __shared_count(in_place_type_t<_Real>, _Tp*& __p, + _Sp_alloc_shared_tag<_Alloc> __a, _Args&&... __args) + { + this->__sized_allocate<_Real>(__p, std::move(__a), + std::forward<_Args>(__args)...); } +#endif // C++20 and above make/allocate_shared array support #if _GLIBCXX_USE_DEPRECATED #pragma GCC diagnostic push @@ -788,6 +1018,77 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: friend class __weak_count<_Lp>; + template <typename _Tp, typename _Alloc, typename... _Args> + void __single_allocate (_Tp*& __p, _Sp_alloc_shared_tag<_Alloc> __a, + _Args&&... __args) + { + typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type; + typename _Sp_cp_type::__allocator_type __a2(__a._M_a); + auto __guard = std::__allocate_guarded(__a2); + _Sp_cp_type* __mem = __guard.get(); + auto __pi = ::new (__mem) + _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...); + __guard = nullptr; + _M_pi = __pi; + __p = __pi->_M_ptr(); + } + +#if __cplusplus > 201703L + template<typename _Real, typename _Tp, + typename _Alloc, typename... _Args> + requires (is_unbounded_array_v<_Real>) + void __sized_allocate (_Tp*& __p, + _Sp_alloc_shared_tag<_Alloc> __a, + __alloc_size_t<_Alloc> __num_elements, + _Args&&... __args) + { + using _Sp_cp_type = + _Sp_counted_sized_ptr_inplace<_Tp, _Alloc, 0, _Lp>; + typename _Sp_cp_type::__allocator_type __a2(__a._M_a); + __alloc_size<_Alloc> __impl_size + = _Sp_cp_type::_S_Impl_size(__num_elements); + auto __guard = std::__allocate_guarded_size(__a2, __impl_size); + _Sp_cp_type* __mem = reinterpret_cast<_Sp_cp_type*>(__guard.get()); + auto __pi = + ::new (__mem) _Sp_cp_type(__a._M_a, __num_elements, + std::forward<_Args>(__args)...); + __guard = nullptr; + _M_pi = __pi; + __p = __pi->_M_ptr(); + } + + template<typename _Real, typename _Tp, + typename _Alloc, typename... _Args> + requires (is_bounded_array_v<_Real>) + void __sized_allocate (_Tp*& __p, + _Sp_alloc_shared_tag<_Alloc> __a, + _Args&&... __args) + { + using _Sp_cp_type = + _Sp_counted_sized_ptr_inplace<remove_cv_t<_Real> + , _Alloc, extent_v<remove_cv_t<_Real>>, _Lp>; + typename _Sp_cp_type::__allocator_type __a2(__a._M_a); + auto __guard = std::__allocate_guarded(__a2); + _Sp_cp_type* __mem = __guard.get(); + auto __pi = + ::new (__mem) _Sp_cp_type(__a._M_a, extent_v<remove_cv_t<_Real>>, + std::forward<_Args>(__args)...); + __guard = nullptr; + _M_pi = __pi; + __p = __pi->_M_ptr(); + } + + template<typename _Real, typename _Tp, + typename _Alloc, typename... _Args> + requires (!is_array_v<_Real>) + void __sized_allocate (_Tp*& __p, + _Sp_alloc_shared_tag<_Alloc> __a, + _Args&&... __args) + { + this->__single_allocate (__p, __a, std::forward<_Args>(__args)...); + } +#endif // C++20 and above make/allocate_shared array support + _Sp_counted_base<_Lp>* _M_pi; }; @@ -1362,11 +1663,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // @} protected: - // This constructor is non-standard, it is used by allocate_shared. + // This constructor is non-standard, it is used by __allocate_shared. template<typename _Alloc, typename... _Args> __shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args) : _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...) { _M_enable_shared_from_this_with(_M_ptr); } + +#if __cplusplus > 201703L + // This constructor is non-standard, it is used by allocate_shared. + template<typename _Alloc, typename... _Args> + __shared_ptr(in_place_type_t<_Tp> __type_tag, + _Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args) + : _M_ptr(), + _M_refcount(__type_tag, _M_ptr, __tag, std::forward<_Args>(__args)...) + { _M_enable_shared_from_this_with(_M_ptr); } +#endif // C++20 and above make/allocate_shared array support template<typename _Tp1, _Lock_policy _Lp1, typename _Alloc, typename... _Args> diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h index 5c9a84d9497..914be803025 100644 --- a/libstdc++-v3/include/bits/stl_construct.h +++ b/libstdc++-v3/include/bits/stl_construct.h @@ -60,6 +60,9 @@ #include <bits/move.h> #include <bits/stl_iterator_base_types.h> // for iterator_traits #include <bits/stl_iterator_base_funcs.h> // for advance +#if __cplusplus > 201703L +#include <array> +#endif // C++20 array construction helpers /* This file provides the C++17 functions std::destroy_at, std::destroy, and * std::destroy_n, and the C++20 function std::construct_at. @@ -163,8 +166,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct _Destroy_aux<true> { template<typename _ForwardIterator> - static void - __destroy(_ForwardIterator, _ForwardIterator) { } + static void + __destroy(_ForwardIterator, _ForwardIterator) { } }; /** @@ -177,7 +180,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Destroy(_ForwardIterator __first, _ForwardIterator __last) { typedef typename iterator_traits<_ForwardIterator>::value_type - _Value_type; + _Value_type; #if __cplusplus >= 201103L // A deleted destructor is trivial, this ensures we reject such types: static_assert(is_destructible<_Value_type>::value, @@ -208,8 +211,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct _Destroy_n_aux<true> { template<typename _ForwardIterator, typename _Size> - static _ForwardIterator - __destroy_n(_ForwardIterator __first, _Size __count) + static _ForwardIterator + __destroy_n(_ForwardIterator __first, _Size __count) { std::advance(__first, __count); return __first; @@ -226,7 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Destroy_n(_ForwardIterator __first, _Size __count) { typedef typename iterator_traits<_ForwardIterator>::value_type - _Value_type; + _Value_type; #if __cplusplus >= 201103L // A deleted destructor is trivial, this ensures we reject such types: static_assert(is_destructible<_Value_type>::value, diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc new file mode 100644 index 00000000000..23f0cc702f8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2007-2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++2a } } + +#include <memory> +#include <cstddef> +#include <cstring> +#include <vector> +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +// p0674 - Extending make_shared support for Arrays +// [util.smartptr.shared.create] - C++2a + +void +test01() +{ + const double expected{}; + const std::size_t count = 124; + __gnu_test::tracker_allocator<double> alloc; + std::shared_ptr<double[]> p + = std::allocate_shared<double[]>(alloc, count); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( p[i] == expected ); + } +} + +void +test02() +{ + const int expected[2][2]{}; + const std::size_t count = 84; + __gnu_test::tracker_allocator<int[2][2]> alloc{}; + std::shared_ptr<int[][2][2]> p + = std::allocate_shared<int[][2][2]>(alloc, count); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test03() +{ + const double expected[6]{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; + const std::size_t count = 1; + __gnu_test::tracker_allocator<double[6]> alloc{}; + std::shared_ptr<double[6]> p + = std::allocate_shared<double[6]>(alloc, 1.0); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test04() +{ + const double expected[46]{}; + const std::size_t count = 1; + __gnu_test::tracker_allocator<double[46]> alloc{}; + std::shared_ptr<double[46]> p + = std::allocate_shared<double[46]>(alloc); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test05() +{ + const unsigned char expected[6][2][2]{}; + const std::size_t count = 1; + __gnu_test::tracker_allocator<unsigned char[6][2][2]> alloc{}; + std::shared_ptr<unsigned char[6][2][2]> p + = std::allocate_shared<unsigned char[6][2][2]>(alloc); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test06() +{ + const short expected[2][2]{{1, 0}, {0, 1}}; + const std::size_t count = 6; + __gnu_test::tracker_allocator<short[6][2][2]> alloc{}; + std::shared_ptr<short[6][2][2]> p + = std::allocate_shared<short[6][2][2]>(alloc, {{1, 0}, {0, 1}}); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i][0], &expected, sizeof (expected)) == 0 ); + } +} + +void +test07() +{ + const std::vector<int> expected{1, 2}; + const std::size_t count = 4; + __gnu_test::tracker_allocator<std::vector<int>[4]> alloc{}; + std::shared_ptr<std::vector<int>[4]> p + = std::allocate_shared<std::vector<int>[4]>(alloc, {1, 2}); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( p[i] == expected ); + } +} + +void +test08() +{ + const std::vector<int> expected{1, 2}; + const std::size_t count = 4; + __gnu_test::tracker_allocator<std::vector<int>> alloc{}; + std::shared_ptr<std::vector<int>[]> p = + std::allocate_shared<std::vector<int>[]>(alloc, count, {1, 2}); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( p[i] == expected ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); + test08(); +} diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.make.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.make.cc new file mode 100644 index 00000000000..94ef72829ce --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.make.cc @@ -0,0 +1,144 @@ +// Copyright (C) 2007-2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++2a } } + +#include <memory> +#include <cstddef> +#include <cstring> +#include <vector> +#include <testsuite_hooks.h> + +// p0674 - Extending make_shared support for Arrays +// [util.smartptr.shared.create] - C++2a + +void +test01() +{ + const double expected{}; + const std::size_t count = 124; + std::shared_ptr<double[]> p + = std::make_shared<double[]>(count); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( p[i] == expected ); + } +} + +void +test02() +{ + const int expected[2][2]{}; + const std::size_t count = 84; + std::shared_ptr<int[][2][2]> p + = std::make_shared<int[][2][2]>(count); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test03() +{ + const double expected[6]{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; + const std::size_t count = 1; + std::shared_ptr<double[6]> p + = std::make_shared<double[6]>(1.0); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test04() +{ + const double expected[46]{}; + const std::size_t count = 1; + std::shared_ptr<double[46]> p + = std::make_shared<double[46]>(); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test05() +{ + const unsigned char expected[6][2][2]{}; + const std::size_t count = 1; + std::shared_ptr<unsigned char[6][2][2]> p + = std::make_shared<unsigned char[6][2][2]>(); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 ); + } +} + +void +test06() +{ + const short expected[2][2]{{1, 0}, {0, 1}}; + const std::size_t count = 6; + std::shared_ptr<short[6][2][2]> p + = std::make_shared<short[6][2][2]>({{1, 0}, {0, 1}}); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( std::memcmp(&p[i][0], &expected, sizeof (expected)) == 0 ); + } +} + +void +test07() +{ + const std::vector<int> expected{1, 2}; + const std::size_t count = 4; + std::shared_ptr<std::vector<int>[4]> p + = std::make_shared<std::vector<int>[4]>({1, 2}); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( p[i] == expected ); + } +} + +void +test08() +{ + const std::vector<int> expected{1, 2}; + const std::size_t count = 4; + std::shared_ptr<std::vector<int>[]> p = + std::make_shared<std::vector<int>[]>(count, {1, 2}); + for (std::size_t i = 0; i < count; ++i) + { + VERIFY( p[i] == expected ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); + test08(); +}