| 1 | // Guarded Allocation -*- C++ -*- |
| 2 | |
| 3 | // Copyright (C) 2014-2026 Free Software Foundation, Inc. |
| 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 bits/allocated_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 _ALLOCATED_PTR_H |
| 31 | #define _ALLOCATED_PTR_H 1 |
| 32 | |
| 33 | #if __cplusplus < 201103L |
| 34 | # include <bits/c++0xwarning.h> |
| 35 | #else |
| 36 | # include <type_traits> |
| 37 | # include <bits/ptr_traits.h> |
| 38 | # include <bits/alloc_traits.h> |
| 39 | # include <bits/utility.h> |
| 40 | |
| 41 | namespace std _GLIBCXX_VISIBILITY(default) |
| 42 | { |
| 43 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| 44 | /// @cond undocumented |
| 45 | |
| 46 | /// Non-standard RAII type for managing pointers obtained from allocators. |
| 47 | template<typename _Alloc> |
| 48 | struct __allocated_ptr |
| 49 | { |
| 50 | using pointer = typename allocator_traits<_Alloc>::pointer; |
| 51 | using value_type = typename allocator_traits<_Alloc>::value_type; |
| 52 | |
| 53 | /// Take ownership of __ptr |
| 54 | __allocated_ptr(_Alloc& __a, pointer __ptr) noexcept |
| 55 | : _M_alloc(std::__addressof(__a)), _M_ptr(__ptr) |
| 56 | { } |
| 57 | |
| 58 | /// Convert __ptr to allocator's pointer type and take ownership of it |
| 59 | template<typename _Ptr, |
| 60 | typename _Req = _Require<is_same<_Ptr, value_type*>>> |
| 61 | __allocated_ptr(_Alloc& __a, _Ptr __ptr) |
| 62 | : _M_alloc(std::__addressof(__a)), |
| 63 | _M_ptr(pointer_traits<pointer>::pointer_to(*__ptr)) |
| 64 | { } |
| 65 | |
| 66 | /// Transfer ownership of the owned pointer |
| 67 | __allocated_ptr(__allocated_ptr&& __gd) noexcept |
| 68 | : _M_alloc(__gd._M_alloc), _M_ptr(__gd._M_ptr) |
| 69 | { __gd._M_ptr = nullptr; } |
| 70 | |
| 71 | /// Deallocate the owned pointer |
| 72 | ~__allocated_ptr() |
| 73 | { |
| 74 | if (_M_ptr != nullptr) |
| 75 | std::allocator_traits<_Alloc>::deallocate(*_M_alloc, _M_ptr, 1); |
| 76 | } |
| 77 | |
| 78 | /// Release ownership of the owned pointer |
| 79 | __allocated_ptr& |
| 80 | operator=(std::nullptr_t) noexcept |
| 81 | { |
| 82 | _M_ptr = nullptr; |
| 83 | return *this; |
| 84 | } |
| 85 | |
| 86 | explicit operator bool() const noexcept { return (bool)_M_ptr; } |
| 87 | |
| 88 | /// Get the address that the owned pointer refers to. |
| 89 | value_type* get() const { return std::__to_address(_M_ptr); } |
| 90 | |
| 91 | pointer release() { return std::__exchange(_M_ptr, nullptr); } |
| 92 | |
| 93 | private: |
| 94 | _Alloc* _M_alloc; |
| 95 | pointer _M_ptr; |
| 96 | }; |
| 97 | |
| 98 | /// Allocate space for a single object using __a. |
| 99 | template<typename _Alloc> |
| 100 | inline __allocated_ptr<_Alloc> |
| 101 | __allocate_guarded(_Alloc& __a) |
| 102 | { |
| 103 | return { __a, std::allocator_traits<_Alloc>::allocate(__a, 1) }; |
| 104 | } |
| 105 | |
| 106 | /// RAII type for constructing/destroying an object with an allocated pointer |
| 107 | template<typename _Alloc> |
| 108 | struct __allocated_obj : __allocated_ptr<_Alloc> |
| 109 | { |
| 110 | using value_type = typename __allocated_ptr<_Alloc>::value_type; |
| 111 | |
| 112 | __allocated_obj(__allocated_obj<_Alloc>&&) = default; |
| 113 | |
| 114 | // Default-initialize a value_type at *__ptr |
| 115 | __allocated_obj(__allocated_ptr<_Alloc>&& __ptr) |
| 116 | : __allocated_ptr<_Alloc>(std::move(__ptr)) |
| 117 | { ::new ((void*)this->get()) value_type; } |
| 118 | |
| 119 | // Call the destructor if an object is owned. |
| 120 | ~__allocated_obj() |
| 121 | { |
| 122 | if (static_cast<bool>(*this)) |
| 123 | this->get()->~value_type(); |
| 124 | } |
| 125 | |
| 126 | using __allocated_ptr<_Alloc>::operator=; |
| 127 | |
| 128 | value_type& operator*() const { return *this->get(); } |
| 129 | value_type* operator->() const { return this->get(); } |
| 130 | }; |
| 131 | |
| 132 | /// Construct an object in storage allocated using __a. |
| 133 | template<typename _Alloc> |
| 134 | inline __allocated_obj<_Alloc> |
| 135 | __allocate_guarded_obj(_Alloc& __a) |
| 136 | { |
| 137 | return { std::__allocate_guarded(__a) }; |
| 138 | } |
| 139 | |
| 140 | // An RAII type that acquires memory from an allocator. |
| 141 | // N.B. 'scoped' here in in the RAII sense, not the scoped allocator model, |
| 142 | // so this has nothing to do with `std::scoped_allocator_adaptor`. |
| 143 | // This class can be used to simplify the common pattern: |
| 144 | // |
| 145 | // auto ptr = alloc.allocate(1); |
| 146 | // try { |
| 147 | // std::construct_at(std::to_address(ptr), args); |
| 148 | // m_ptr = ptr; |
| 149 | // } catch (...) { |
| 150 | // alloc.deallocate(ptr, 1); |
| 151 | // throw; |
| 152 | // } |
| 153 | // |
| 154 | // Instead you can do: |
| 155 | // |
| 156 | // _Scoped_allocation sa(alloc); |
| 157 | // m_ptr = std::construct_at(sa.get(), args); |
| 158 | // (void) sa.release(); |
| 159 | // |
| 160 | // Or even simpler: |
| 161 | // |
| 162 | // _Scoped_allocation sa(alloc, std::in_place, args); |
| 163 | // m_ptr = sa.release(); |
| 164 | // |
| 165 | template<typename _Alloc> |
| 166 | struct _Scoped_allocation |
| 167 | { |
| 168 | using value_type = typename allocator_traits<_Alloc>::value_type; |
| 169 | using pointer = typename allocator_traits<_Alloc>::pointer; |
| 170 | |
| 171 | // Use `a` to allocate memory for `n` objects. |
| 172 | constexpr explicit |
| 173 | _Scoped_allocation(const _Alloc& __a, size_t __n = 1) |
| 174 | : _M_a(__a), _M_n(__n), _M_p(_M_a.allocate(__n)) |
| 175 | { } |
| 176 | |
| 177 | #if __glibcxx_optional >= 201606L |
| 178 | // Allocate memory for a single object and if that succeeds, |
| 179 | // construct an object using args. |
| 180 | // |
| 181 | // Does not do uses-allocator construction; don't use if you need that. |
| 182 | // |
| 183 | // CAUTION: the destructor will *not* destroy this object, it will only |
| 184 | // free the memory. That means the following pattern is unsafe: |
| 185 | // |
| 186 | // _Scoped_allocation sa(alloc, in_place, args); |
| 187 | // potentially_throwing_operations(); |
| 188 | // return sa.release(); |
| 189 | // |
| 190 | // If the middle operation throws, the object will not be destroyed. |
| 191 | template<typename... _Args> |
| 192 | constexpr explicit |
| 193 | _Scoped_allocation(const _Alloc& __a, in_place_t, _Args&&... __args) |
| 194 | : _Scoped_allocation(__a, 1) |
| 195 | { |
| 196 | // The target constructor has completed, so if the next line throws, |
| 197 | // the destructor will deallocate the memory. |
| 198 | allocator_traits<_Alloc>::construct(_M_a, get(), |
| 199 | std::forward<_Args>(__args)...); |
| 200 | } |
| 201 | #endif |
| 202 | |
| 203 | _GLIBCXX20_CONSTEXPR |
| 204 | ~_Scoped_allocation() |
| 205 | { |
| 206 | if (_M_p) [[__unlikely__]] |
| 207 | _M_a.deallocate(_M_p, _M_n); |
| 208 | } |
| 209 | |
| 210 | _Scoped_allocation(_Scoped_allocation&&) = delete; |
| 211 | |
| 212 | constexpr _Alloc |
| 213 | get_allocator() const noexcept { return _M_a; } |
| 214 | |
| 215 | constexpr value_type* |
| 216 | get() const noexcept |
| 217 | { return std::__to_address(_M_p); } |
| 218 | |
| 219 | [[__nodiscard__]] |
| 220 | constexpr pointer |
| 221 | release() noexcept { return std::__exchange(_M_p, nullptr); } |
| 222 | |
| 223 | private: |
| 224 | [[__no_unique_address__]] _Alloc _M_a; |
| 225 | size_t _M_n; |
| 226 | pointer _M_p; |
| 227 | }; |
| 228 | |
| 229 | #if __glibcxx_optional >= 201606L && __cpp_deduction_guides >= 201606L |
| 230 | template<typename _Alloc, typename... _Args> |
| 231 | _Scoped_allocation(_Alloc, in_place_t, _Args...) |
| 232 | -> _Scoped_allocation<_Alloc>; |
| 233 | #endif |
| 234 | |
| 235 | /// @endcond |
| 236 | _GLIBCXX_END_NAMESPACE_VERSION |
| 237 | } // namespace std |
| 238 | |
| 239 | #endif |
| 240 | #endif |
| 241 | |