1 | // Smart pointer adaptors -*- C++ -*- |
2 | |
3 | // Copyright The GNU Toolchain Authors. |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free |
6 | // software; you can redistribute it and/or modify it under the |
7 | // terms of the GNU General Public License as published by the |
8 | // Free Software Foundation; either version 3, or (at your option) |
9 | // any later version. |
10 | |
11 | // This library is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | |
16 | // Under Section 7 of GPL version 3, you are granted additional |
17 | // permissions described in the GCC Runtime Library Exception, version |
18 | // 3.1, as published by the Free Software Foundation. |
19 | |
20 | // You should have received a copy of the GNU General Public License and |
21 | // a copy of the GCC Runtime Library Exception along with this program; |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
23 | // <http://www.gnu.org/licenses/>. |
24 | |
25 | /** @file include/bits/out_ptr.h |
26 | * This is an internal header file, included by other library headers. |
27 | * Do not attempt to use it directly. @headername{memory} |
28 | */ |
29 | |
30 | #ifndef _GLIBCXX_OUT_PTR_H |
31 | #define _GLIBCXX_OUT_PTR_H 1 |
32 | |
33 | #pragma GCC system_header |
34 | |
35 | #include <bits/version.h> |
36 | |
37 | #ifdef __glibcxx_out_ptr // C++ >= 23 |
38 | |
39 | #include <tuple> |
40 | #include <bits/ptr_traits.h> |
41 | |
42 | namespace std _GLIBCXX_VISIBILITY(default) |
43 | { |
44 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
45 | |
46 | /// Smart pointer adaptor for functions taking an output pointer parameter. |
47 | /** |
48 | * @tparam _Smart The type of pointer to adapt. |
49 | * @tparam _Pointer The type of pointer to convert to. |
50 | * @tparam _Args... Argument types used when resetting the smart pointer. |
51 | * @since C++23 |
52 | * @headerfile <memory> |
53 | */ |
54 | template<typename _Smart, typename _Pointer, typename... _Args> |
55 | class out_ptr_t |
56 | { |
57 | #if _GLIBCXX_HOSTED |
58 | static_assert(!__is_shared_ptr<_Smart> || sizeof...(_Args) != 0, |
59 | "a deleter must be used when adapting std::shared_ptr " |
60 | "with std::out_ptr" ); |
61 | #endif |
62 | |
63 | public: |
64 | explicit |
65 | out_ptr_t(_Smart& __smart, _Args... __args) |
66 | : _M_impl{__smart, std::forward<_Args>(__args)...} |
67 | { |
68 | if constexpr (requires { _M_impl._M_out_init(); }) |
69 | _M_impl._M_out_init(); |
70 | } |
71 | |
72 | out_ptr_t(const out_ptr_t&) = delete; |
73 | |
74 | ~out_ptr_t() = default; |
75 | |
76 | operator _Pointer*() const noexcept |
77 | { return _M_impl._M_get(); } |
78 | |
79 | operator void**() const noexcept requires (!same_as<_Pointer, void*>) |
80 | { |
81 | static_assert(is_pointer_v<_Pointer>); |
82 | _Pointer* __p = *this; |
83 | return static_cast<void**>(static_cast<void*>(__p)); |
84 | } |
85 | |
86 | private: |
87 | // TODO: Move this to namespace scope? e.g. __detail::_Ptr_adapt_impl |
88 | template<typename, typename, typename...> |
89 | struct _Impl |
90 | { |
91 | // This constructor must not modify __s because out_ptr_t and |
92 | // inout_ptr_t want to do different things. After construction |
93 | // they call _M_out_init() or _M_inout_init() respectively. |
94 | _Impl(_Smart& __s, _Args&&... __args) |
95 | : _M_smart(__s), _M_args(std::forward<_Args>(__args)...) |
96 | { } |
97 | |
98 | // Called by out_ptr_t to clear the smart pointer before using it. |
99 | void |
100 | _M_out_init() |
101 | { |
102 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
103 | // 3734. Inconsistency in inout_ptr and out_ptr for empty case |
104 | if constexpr (requires { _M_smart.reset(); }) |
105 | _M_smart.reset(); |
106 | else |
107 | _M_smart = _Smart(); |
108 | } |
109 | |
110 | // Called by inout_ptr_t to copy the smart pointer's value |
111 | // to the pointer that is returned from _M_get(). |
112 | void |
113 | _M_inout_init() |
114 | { _M_ptr = _M_smart.release(); } |
115 | |
116 | // The pointer value returned by operator Pointer*(). |
117 | _Pointer* |
118 | _M_get() const |
119 | { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); } |
120 | |
121 | // Finalize the effects on the smart pointer. |
122 | ~_Impl() noexcept(false); |
123 | |
124 | _Smart& _M_smart; |
125 | [[no_unique_address]] _Pointer _M_ptr{}; |
126 | [[no_unique_address]] tuple<_Args...> _M_args; |
127 | }; |
128 | |
129 | // Partial specialization for raw pointers. |
130 | template<typename _Tp> |
131 | struct _Impl<_Tp*, _Tp*> |
132 | { |
133 | void |
134 | _M_out_init() |
135 | { _M_p = nullptr; } |
136 | |
137 | void |
138 | _M_inout_init() |
139 | { } |
140 | |
141 | _Tp** |
142 | _M_get() const |
143 | { return __builtin_addressof(const_cast<_Tp*&>(_M_p)); } |
144 | |
145 | _Tp*& _M_p; |
146 | }; |
147 | |
148 | // Partial specialization for raw pointers, with conversion. |
149 | template<typename _Tp, typename _Ptr> requires (!is_same_v<_Ptr, _Tp*>) |
150 | struct _Impl<_Tp*, _Ptr> |
151 | { |
152 | explicit |
153 | _Impl(_Tp*& __p) |
154 | : _M_p(__p) |
155 | { } |
156 | |
157 | void |
158 | _M_out_init() |
159 | { _M_p = nullptr; } |
160 | |
161 | void |
162 | _M_inout_init() |
163 | { _M_ptr = _M_p; } |
164 | |
165 | _Pointer* |
166 | _M_get() const |
167 | { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); } |
168 | |
169 | ~_Impl() { _M_p = static_cast<_Tp*>(_M_ptr); } |
170 | |
171 | _Tp*& _M_p; |
172 | _Pointer _M_ptr{}; |
173 | }; |
174 | |
175 | // Partial specialization for std::unique_ptr. |
176 | // This specialization gives direct access to the private member |
177 | // of the unique_ptr, avoiding the overhead of storing a separate |
178 | // pointer and then resetting the unique_ptr in the destructor. |
179 | // FIXME: constrain to only match the primary template, |
180 | // not program-defined specializations of unique_ptr. |
181 | template<typename _Tp, typename _Del> |
182 | struct _Impl<unique_ptr<_Tp, _Del>, |
183 | typename unique_ptr<_Tp, _Del>::pointer> |
184 | { |
185 | void |
186 | _M_out_init() |
187 | { _M_smart.reset(); } |
188 | |
189 | _Pointer* |
190 | _M_get() const noexcept |
191 | { return __builtin_addressof(_M_smart._M_t._M_ptr()); } |
192 | |
193 | _Smart& _M_smart; |
194 | }; |
195 | |
196 | // Partial specialization for std::unique_ptr with replacement deleter. |
197 | // FIXME: constrain to only match the primary template, |
198 | // not program-defined specializations of unique_ptr. |
199 | template<typename _Tp, typename _Del, typename _Del2> |
200 | struct _Impl<unique_ptr<_Tp, _Del>, |
201 | typename unique_ptr<_Tp, _Del>::pointer, _Del2> |
202 | { |
203 | void |
204 | _M_out_init() |
205 | { _M_smart.reset(); } |
206 | |
207 | _Pointer* |
208 | _M_get() const noexcept |
209 | { return __builtin_addressof(_M_smart._M_t._M_ptr()); } |
210 | |
211 | ~_Impl() |
212 | { |
213 | if (_M_smart.get()) |
214 | _M_smart._M_t._M_deleter() = std::forward<_Del2>(_M_del); |
215 | } |
216 | |
217 | _Smart& _M_smart; |
218 | [[no_unique_address]] _Del2 _M_del; |
219 | }; |
220 | |
221 | #if _GLIBCXX_HOSTED |
222 | // Partial specialization for std::shared_ptr. |
223 | // This specialization gives direct access to the private member |
224 | // of the shared_ptr, avoiding the overhead of storing a separate |
225 | // pointer and then resetting the shared_ptr in the destructor. |
226 | // A new control block is allocated in the constructor, so that if |
227 | // allocation fails it doesn't throw an exception from the destructor. |
228 | template<typename _Tp, typename _Del, typename _Alloc> |
229 | requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>) |
230 | struct _Impl<shared_ptr<_Tp>, |
231 | typename shared_ptr<_Tp>::element_type*, _Del, _Alloc> |
232 | { |
233 | _Impl(_Smart& __s, _Del __d, _Alloc __a = _Alloc()) |
234 | : _M_smart(__s) |
235 | { |
236 | // We know shared_ptr cannot be used with inout_ptr_t |
237 | // so we can do all set up here, instead of in _M_out_init(). |
238 | _M_smart.reset(); |
239 | |
240 | // Similar to the shared_ptr(Y*, D, A) constructor, except that if |
241 | // the allocation throws we do not need (or want) to call deleter. |
242 | typename _Scd::__allocator_type __a2(__a); |
243 | auto __mem = __a2.allocate(1); |
244 | ::new (__mem) _Scd(nullptr, std::forward<_Del>(__d), |
245 | std::forward<_Alloc>(__a)); |
246 | _M_smart._M_refcount._M_pi = __mem; |
247 | } |
248 | |
249 | _Pointer* |
250 | _M_get() const noexcept |
251 | { return __builtin_addressof(_M_smart._M_ptr); } |
252 | |
253 | ~_Impl() |
254 | { |
255 | auto& __pi = _M_smart._M_refcount._M_pi; |
256 | |
257 | if (_Sp __ptr = _M_smart.get()) |
258 | static_cast<_Scd*>(__pi)->_M_impl._M_ptr = __ptr; |
259 | else // Destroy the control block manually without invoking deleter. |
260 | std::__exchange(__pi, nullptr)->_M_destroy(); |
261 | } |
262 | |
263 | _Smart& _M_smart; |
264 | |
265 | using _Sp = typename _Smart::element_type*; |
266 | using _Scd = _Sp_counted_deleter<_Sp, decay_t<_Del>, |
267 | remove_cvref_t<_Alloc>, |
268 | __default_lock_policy>; |
269 | }; |
270 | |
271 | // Partial specialization for std::shared_ptr, without custom allocator. |
272 | template<typename _Tp, typename _Del> |
273 | requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>) |
274 | struct _Impl<shared_ptr<_Tp>, |
275 | typename shared_ptr<_Tp>::element_type*, _Del> |
276 | : _Impl<_Smart, _Pointer, _Del, allocator<void>> |
277 | { |
278 | using _Impl<_Smart, _Pointer, _Del, allocator<void>>::_Impl; |
279 | }; |
280 | #endif |
281 | |
282 | using _Impl_t = _Impl<_Smart, _Pointer, _Args...>; |
283 | |
284 | _Impl_t _M_impl; |
285 | |
286 | template<typename, typename, typename...> friend class inout_ptr_t; |
287 | }; |
288 | |
289 | /// Smart pointer adaptor for functions taking an inout pointer parameter. |
290 | /** |
291 | * @tparam _Smart The type of pointer to adapt. |
292 | * @tparam _Pointer The type of pointer to convert to. |
293 | * @tparam _Args... Argument types used when resetting the smart pointer. |
294 | * @since C++23 |
295 | * @headerfile <memory> |
296 | */ |
297 | template<typename _Smart, typename _Pointer, typename... _Args> |
298 | class inout_ptr_t |
299 | { |
300 | #if _GLIBCXX_HOSTED |
301 | static_assert(!__is_shared_ptr<_Smart>, |
302 | "std::inout_ptr can not be used to wrap std::shared_ptr" ); |
303 | #endif |
304 | |
305 | public: |
306 | explicit |
307 | inout_ptr_t(_Smart& __smart, _Args... __args) |
308 | : _M_impl{__smart, std::forward<_Args>(__args)...} |
309 | { |
310 | if constexpr (requires { _M_impl._M_inout_init(); }) |
311 | _M_impl._M_inout_init(); |
312 | } |
313 | |
314 | inout_ptr_t(const inout_ptr_t&) = delete; |
315 | |
316 | ~inout_ptr_t() = default; |
317 | |
318 | operator _Pointer*() const noexcept |
319 | { return _M_impl._M_get(); } |
320 | |
321 | operator void**() const noexcept requires (!same_as<_Pointer, void*>) |
322 | { |
323 | static_assert(is_pointer_v<_Pointer>); |
324 | _Pointer* __p = *this; |
325 | return static_cast<void**>(static_cast<void*>(__p)); |
326 | } |
327 | |
328 | private: |
329 | #if _GLIBCXX_HOSTED |
330 | // Avoid an invalid instantiation of out_ptr_t<shared_ptr<T>, ...> |
331 | using _Out_ptr_t |
332 | = __conditional_t<__is_shared_ptr<_Smart>, |
333 | out_ptr_t<void*, void*>, |
334 | out_ptr_t<_Smart, _Pointer, _Args...>>; |
335 | #else |
336 | using _Out_ptr_t = out_ptr_t<_Smart, _Pointer, _Args...>; |
337 | #endif |
338 | using _Impl_t = typename _Out_ptr_t::_Impl_t; |
339 | _Impl_t _M_impl; |
340 | }; |
341 | |
342 | /// @cond undocumented |
343 | namespace __detail |
344 | { |
345 | // POINTER_OF metafunction |
346 | template<typename _Tp> |
347 | consteval auto |
348 | __pointer_of() |
349 | { |
350 | if constexpr (requires { typename _Tp::pointer; }) |
351 | return type_identity<typename _Tp::pointer>{}; |
352 | else if constexpr (requires { typename _Tp::element_type; }) |
353 | return type_identity<typename _Tp::element_type*>{}; |
354 | else |
355 | { |
356 | using _Traits = pointer_traits<_Tp>; |
357 | if constexpr (requires { typename _Traits::element_type; }) |
358 | return type_identity<typename _Traits::element_type*>{}; |
359 | } |
360 | // else POINTER_OF(S) is not a valid type, return void. |
361 | } |
362 | |
363 | // POINTER_OF_OR metafunction |
364 | template<typename _Smart, typename _Ptr> |
365 | consteval auto |
366 | __pointer_of_or() |
367 | { |
368 | using _TypeId = decltype(__detail::__pointer_of<_Smart>()); |
369 | if constexpr (is_void_v<_TypeId>) |
370 | return type_identity<_Ptr>{}; |
371 | else |
372 | return _TypeId{}; |
373 | } |
374 | |
375 | // Returns Pointer if !is_void_v<Pointer>, otherwise POINTER_OF(Smart). |
376 | template<typename _Ptr, typename _Smart> |
377 | consteval auto |
378 | __choose_ptr() |
379 | { |
380 | if constexpr (!is_void_v<_Ptr>) |
381 | return type_identity<_Ptr>{}; |
382 | else |
383 | return __detail::__pointer_of<_Smart>(); |
384 | } |
385 | |
386 | template<typename _Smart, typename _Sp, typename... _Args> |
387 | concept __resettable = requires (_Smart& __s) { |
388 | __s.reset(std::declval<_Sp>(), std::declval<_Args>()...); |
389 | }; |
390 | } |
391 | /// @endcond |
392 | |
393 | /// Adapt a smart pointer for functions taking an output pointer parameter. |
394 | /** |
395 | * @tparam _Pointer The type of pointer to convert to. |
396 | * @param __s The pointer that should take ownership of the result. |
397 | * @param __args... Arguments to use when resetting the smart pointer. |
398 | * @return A std::inout_ptr_t referring to `__s`. |
399 | * @since C++23 |
400 | * @headerfile <memory> |
401 | */ |
402 | template<typename _Pointer = void, typename _Smart, typename... _Args> |
403 | inline auto |
404 | out_ptr(_Smart& __s, _Args&&... __args) |
405 | { |
406 | using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>()); |
407 | static_assert(!is_void_v<_TypeId>, "first argument to std::out_ptr " |
408 | "must be a pointer-like type" ); |
409 | |
410 | using _Ret = out_ptr_t<_Smart, typename _TypeId::type, _Args&&...>; |
411 | return _Ret(__s, std::forward<_Args>(__args)...); |
412 | } |
413 | |
414 | /// Adapt a smart pointer for functions taking an inout pointer parameter. |
415 | /** |
416 | * @tparam _Pointer The type of pointer to convert to. |
417 | * @param __s The pointer that should take ownership of the result. |
418 | * @param __args... Arguments to use when resetting the smart pointer. |
419 | * @return A std::inout_ptr_t referring to `__s`. |
420 | * @since C++23 |
421 | * @headerfile <memory> |
422 | */ |
423 | template<typename _Pointer = void, typename _Smart, typename... _Args> |
424 | inline auto |
425 | inout_ptr(_Smart& __s, _Args&&... __args) |
426 | { |
427 | using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>()); |
428 | static_assert(!is_void_v<_TypeId>, "first argument to std::inout_ptr " |
429 | "must be a pointer-like type" ); |
430 | |
431 | using _Ret = inout_ptr_t<_Smart, typename _TypeId::type, _Args&&...>; |
432 | return _Ret(__s, std::forward<_Args>(__args)...); |
433 | } |
434 | |
435 | /// @cond undocumented |
436 | template<typename _Smart, typename _Pointer, typename... _Args> |
437 | template<typename _Smart2, typename _Pointer2, typename... _Args2> |
438 | inline |
439 | out_ptr_t<_Smart, _Pointer, _Args...>:: |
440 | _Impl<_Smart2, _Pointer2, _Args2...>::~_Impl() |
441 | { |
442 | using _TypeId = decltype(__detail::__pointer_of_or<_Smart, _Pointer>()); |
443 | using _Sp = typename _TypeId::type; |
444 | |
445 | if (!_M_ptr) |
446 | return; |
447 | |
448 | _Smart& __s = _M_smart; |
449 | _Pointer& __p = _M_ptr; |
450 | |
451 | auto __reset = [&](auto&&... __args) { |
452 | if constexpr (__detail::__resettable<_Smart, _Sp, _Args...>) |
453 | __s.reset(static_cast<_Sp>(__p), std::forward<_Args>(__args)...); |
454 | else if constexpr (is_constructible_v<_Smart, _Sp, _Args...>) |
455 | __s = _Smart(static_cast<_Sp>(__p), std::forward<_Args>(__args)...); |
456 | else |
457 | static_assert(is_constructible_v<_Smart, _Sp, _Args...>); |
458 | }; |
459 | |
460 | if constexpr (sizeof...(_Args) >= 2) |
461 | std::apply(__reset, std::move(_M_args)); |
462 | else if constexpr (sizeof...(_Args) == 1) |
463 | __reset(std::get<0>(std::move(_M_args))); |
464 | else |
465 | __reset(); |
466 | } |
467 | /// @endcond |
468 | |
469 | _GLIBCXX_END_NAMESPACE_VERSION |
470 | } // namespace |
471 | |
472 | #endif // __glibcxx_out_ptr |
473 | #endif /* _GLIBCXX_OUT_PTR_H */ |
474 | |