xref: /llvm-project/libcxx/include/__memory_resource/polymorphic_allocator.h (revision 59890c13343af9e308281b3c76bac425087f4f8a)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef _LIBCPP___MEMORY_RESOURCE_POLYMORPHIC_ALLOCATOR_H
10 #define _LIBCPP___MEMORY_RESOURCE_POLYMORPHIC_ALLOCATOR_H
11 
12 #include <__assert>
13 #include <__config>
14 #include <__cstddef/byte.h>
15 #include <__cstddef/max_align_t.h>
16 #include <__fwd/pair.h>
17 #include <__memory_resource/memory_resource.h>
18 #include <__new/exceptions.h>
19 #include <__new/placement_new_delete.h>
20 #include <__utility/exception_guard.h>
21 #include <limits>
22 #include <tuple>
23 
24 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
25 #  pragma GCC system_header
26 #endif
27 
28 _LIBCPP_PUSH_MACROS
29 #include <__undef_macros>
30 
31 #if _LIBCPP_STD_VER >= 17
32 
33 _LIBCPP_BEGIN_NAMESPACE_STD
34 
35 namespace pmr {
36 
37 // [mem.poly.allocator.class]
38 
39 template <class _ValueType
40 #  if _LIBCPP_STD_VER >= 20
41           = byte
42 #  endif
43           >
44 class _LIBCPP_AVAILABILITY_PMR _LIBCPP_TEMPLATE_VIS polymorphic_allocator {
45 
46 public:
47   using value_type = _ValueType;
48 
49   // [mem.poly.allocator.ctor]
50 
51   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator() noexcept : __res_(std::pmr::get_default_resource()) {}
52 
53   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(memory_resource* __r) noexcept : __res_(__r) {}
54 
55   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(const polymorphic_allocator&) = default;
56 
57   template <class _Tp>
58   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(const polymorphic_allocator<_Tp>& __other) noexcept
59       : __res_(__other.resource()) {}
60 
61   polymorphic_allocator& operator=(const polymorphic_allocator&) = delete;
62 
63   // [mem.poly.allocator.mem]
64 
65   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ValueType* allocate(size_t __n) {
66     if (__n > __max_size()) {
67       __throw_bad_array_new_length();
68     }
69     return static_cast<_ValueType*>(__res_->allocate(__n * sizeof(_ValueType), alignof(_ValueType)));
70   }
71 
72   _LIBCPP_HIDE_FROM_ABI void deallocate(_ValueType* __p, size_t __n) {
73     _LIBCPP_ASSERT_VALID_DEALLOCATION(
74         __n <= __max_size(),
75         "deallocate() called for a size which exceeds max_size(), leading to a memory leak "
76         "(the argument will overflow and result in too few objects being deleted)");
77     __res_->deallocate(__p, __n * sizeof(_ValueType), alignof(_ValueType));
78   }
79 
80 #  if _LIBCPP_STD_VER >= 20
81 
82   [[nodiscard]] [[using __gnu__: __alloc_size__(2), __alloc_align__(3)]] _LIBCPP_HIDE_FROM_ABI void*
83   allocate_bytes(size_t __nbytes, size_t __alignment = alignof(max_align_t)) {
84     return __res_->allocate(__nbytes, __alignment);
85   }
86 
87   _LIBCPP_HIDE_FROM_ABI void deallocate_bytes(void* __ptr, size_t __nbytes, size_t __alignment = alignof(max_align_t)) {
88     __res_->deallocate(__ptr, __nbytes, __alignment);
89   }
90 
91   template <class _Type>
92   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _Type* allocate_object(size_t __n = 1) {
93     if (numeric_limits<size_t>::max() / sizeof(_Type) < __n)
94       std::__throw_bad_array_new_length();
95     return static_cast<_Type*>(allocate_bytes(__n * sizeof(_Type), alignof(_Type)));
96   }
97 
98   template <class _Type>
99   _LIBCPP_HIDE_FROM_ABI void deallocate_object(_Type* __ptr, size_t __n = 1) {
100     deallocate_bytes(__ptr, __n * sizeof(_Type), alignof(_Type));
101   }
102 
103   template <class _Type, class... _CtorArgs>
104   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _Type* new_object(_CtorArgs&&... __ctor_args) {
105     _Type* __ptr = allocate_object<_Type>();
106     auto __guard = std::__make_exception_guard([&] { deallocate_object(__ptr); });
107     construct(__ptr, std::forward<_CtorArgs>(__ctor_args)...);
108     __guard.__complete();
109     return __ptr;
110   }
111 
112   template <class _Type>
113   _LIBCPP_HIDE_FROM_ABI void delete_object(_Type* __ptr) {
114     destroy(__ptr);
115     deallocate_object(__ptr);
116   }
117 
118 #  endif // _LIBCPP_STD_VER >= 20
119 
120   template <class _Tp, class... _Ts>
121   _LIBCPP_HIDE_FROM_ABI void construct(_Tp* __p, _Ts&&... __args) {
122     std::__user_alloc_construct_impl(
123         typename __uses_alloc_ctor<_Tp, polymorphic_allocator&, _Ts...>::type(),
124         __p,
125         *this,
126         std::forward<_Ts>(__args)...);
127   }
128 
129   template <class _T1, class _T2, class... _Args1, class... _Args2>
130   _LIBCPP_HIDE_FROM_ABI void
131   construct(pair<_T1, _T2>* __p, piecewise_construct_t, tuple<_Args1...> __x, tuple<_Args2...> __y) {
132     ::new ((void*)__p) pair<_T1, _T2>(
133         piecewise_construct,
134         __transform_tuple(typename __uses_alloc_ctor< _T1, polymorphic_allocator&, _Args1... >::type(),
135                           std::move(__x),
136                           typename __make_tuple_indices<sizeof...(_Args1)>::type{}),
137         __transform_tuple(typename __uses_alloc_ctor< _T2, polymorphic_allocator&, _Args2... >::type(),
138                           std::move(__y),
139                           typename __make_tuple_indices<sizeof...(_Args2)>::type{}));
140   }
141 
142   template <class _T1, class _T2>
143   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p) {
144     construct(__p, piecewise_construct, tuple<>(), tuple<>());
145   }
146 
147   template <class _T1, class _T2, class _Up, class _Vp>
148   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p, _Up&& __u, _Vp&& __v) {
149     construct(__p,
150               piecewise_construct,
151               std::forward_as_tuple(std::forward<_Up>(__u)),
152               std::forward_as_tuple(std::forward<_Vp>(__v)));
153   }
154 
155   template <class _T1, class _T2, class _U1, class _U2>
156   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p, const pair<_U1, _U2>& __pr) {
157     construct(__p, piecewise_construct, std::forward_as_tuple(__pr.first), std::forward_as_tuple(__pr.second));
158   }
159 
160   template <class _T1, class _T2, class _U1, class _U2>
161   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p, pair<_U1, _U2>&& __pr) {
162     construct(__p,
163               piecewise_construct,
164               std::forward_as_tuple(std::forward<_U1>(__pr.first)),
165               std::forward_as_tuple(std::forward<_U2>(__pr.second)));
166   }
167 
168   template <class _Tp>
169   _LIBCPP_HIDE_FROM_ABI void destroy(_Tp* __p) {
170     __p->~_Tp();
171   }
172 
173   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator select_on_container_copy_construction() const noexcept {
174     return polymorphic_allocator();
175   }
176 
177   _LIBCPP_HIDE_FROM_ABI memory_resource* resource() const noexcept { return __res_; }
178 
179   _LIBCPP_HIDE_FROM_ABI friend bool
180   operator==(const polymorphic_allocator& __lhs, const polymorphic_allocator& __rhs) noexcept {
181     return *__lhs.resource() == *__rhs.resource();
182   }
183 
184 #  if _LIBCPP_STD_VER <= 17
185   // This overload is not specified, it was added due to LWG3683.
186   _LIBCPP_HIDE_FROM_ABI friend bool
187   operator!=(const polymorphic_allocator& __lhs, const polymorphic_allocator& __rhs) noexcept {
188     return *__lhs.resource() != *__rhs.resource();
189   }
190 #  endif
191 
192 private:
193   template <class... _Args, size_t... _Is>
194   _LIBCPP_HIDE_FROM_ABI tuple<_Args&&...>
195   __transform_tuple(integral_constant<int, 0>, tuple<_Args...>&& __t, __tuple_indices<_Is...>) {
196     return std::forward_as_tuple(std::get<_Is>(std::move(__t))...);
197   }
198 
199   template <class... _Args, size_t... _Is>
200   _LIBCPP_HIDE_FROM_ABI tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>
201   __transform_tuple(integral_constant<int, 1>, tuple<_Args...>&& __t, __tuple_indices<_Is...>) {
202     using _Tup = tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>;
203     return _Tup(allocator_arg, *this, std::get<_Is>(std::move(__t))...);
204   }
205 
206   template <class... _Args, size_t... _Is>
207   _LIBCPP_HIDE_FROM_ABI tuple<_Args&&..., polymorphic_allocator&>
208   __transform_tuple(integral_constant<int, 2>, tuple<_Args...>&& __t, __tuple_indices<_Is...>) {
209     using _Tup = tuple<_Args&&..., polymorphic_allocator&>;
210     return _Tup(std::get<_Is>(std::move(__t))..., *this);
211   }
212 
213   _LIBCPP_HIDE_FROM_ABI size_t __max_size() const noexcept {
214     return numeric_limits<size_t>::max() / sizeof(value_type);
215   }
216 
217   memory_resource* __res_;
218 };
219 
220 // [mem.poly.allocator.eq]
221 
222 template <class _Tp, class _Up>
223 inline _LIBCPP_HIDE_FROM_ABI bool
224 operator==(const polymorphic_allocator<_Tp>& __lhs, const polymorphic_allocator<_Up>& __rhs) noexcept {
225   return *__lhs.resource() == *__rhs.resource();
226 }
227 
228 #  if _LIBCPP_STD_VER <= 17
229 
230 template <class _Tp, class _Up>
231 inline _LIBCPP_HIDE_FROM_ABI bool
232 operator!=(const polymorphic_allocator<_Tp>& __lhs, const polymorphic_allocator<_Up>& __rhs) noexcept {
233   return !(__lhs == __rhs);
234 }
235 
236 #  endif
237 
238 } // namespace pmr
239 
240 _LIBCPP_END_NAMESPACE_STD
241 
242 #endif // _LIBCPP_STD_VER >= 17
243 
244 _LIBCPP_POP_MACROS
245 
246 #endif // _LIBCPP___MEMORY_RESOURCE_POLYMORPHIC_ALLOCATOR_H
247