| // Guarded Allocation -*- C++ -*- |
| |
| // Copyright (C) 2014-2025 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/allocated_ptr.h |
| * This is an internal header file, included by other library headers. |
| * Do not attempt to use it directly. @headername{memory} |
| */ |
| |
| #ifndef _ALLOCATED_PTR_H |
| #define _ALLOCATED_PTR_H 1 |
| |
| #if __cplusplus < 201103L |
| # include <bits/c++0xwarning.h> |
| #else |
| # include <type_traits> |
| # include <bits/ptr_traits.h> |
| # include <bits/alloc_traits.h> |
| # include <bits/utility.h> |
| |
| namespace std _GLIBCXX_VISIBILITY(default) |
| { |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| /// @cond undocumented |
| |
| /// Non-standard RAII type for managing pointers obtained from allocators. |
| template<typename _Alloc> |
| struct __allocated_ptr |
| { |
| using pointer = typename allocator_traits<_Alloc>::pointer; |
| using value_type = typename allocator_traits<_Alloc>::value_type; |
| |
| /// Take ownership of __ptr |
| __allocated_ptr(_Alloc& __a, pointer __ptr) noexcept |
| : _M_alloc(std::__addressof(__a)), _M_ptr(__ptr) |
| { } |
| |
| /// Convert __ptr to allocator's pointer type and take ownership of it |
| template<typename _Ptr, |
| typename _Req = _Require<is_same<_Ptr, value_type*>>> |
| __allocated_ptr(_Alloc& __a, _Ptr __ptr) |
| : _M_alloc(std::__addressof(__a)), |
| _M_ptr(pointer_traits<pointer>::pointer_to(*__ptr)) |
| { } |
| |
| /// Transfer ownership of the owned pointer |
| __allocated_ptr(__allocated_ptr&& __gd) noexcept |
| : _M_alloc(__gd._M_alloc), _M_ptr(__gd._M_ptr) |
| { __gd._M_ptr = nullptr; } |
| |
| /// Deallocate the owned pointer |
| ~__allocated_ptr() |
| { |
| if (_M_ptr != nullptr) |
| std::allocator_traits<_Alloc>::deallocate(*_M_alloc, _M_ptr, 1); |
| } |
| |
| /// Release ownership of the owned pointer |
| __allocated_ptr& |
| operator=(std::nullptr_t) noexcept |
| { |
| _M_ptr = nullptr; |
| return *this; |
| } |
| |
| explicit operator bool() const noexcept { return (bool)_M_ptr; } |
| |
| /// Get the address that the owned pointer refers to. |
| value_type* get() const { return std::__to_address(_M_ptr); } |
| |
| pointer release() { return std::__exchange(_M_ptr, nullptr); } |
| |
| private: |
| _Alloc* _M_alloc; |
| pointer _M_ptr; |
| }; |
| |
| /// Allocate space for a single object using __a. |
| template<typename _Alloc> |
| inline __allocated_ptr<_Alloc> |
| __allocate_guarded(_Alloc& __a) |
| { |
| return { __a, std::allocator_traits<_Alloc>::allocate(__a, 1) }; |
| } |
| |
| /// RAII type for constructing/destroying an object with an allocated pointer |
| template<typename _Alloc> |
| struct __allocated_obj : __allocated_ptr<_Alloc> |
| { |
| using value_type = typename __allocated_ptr<_Alloc>::value_type; |
| |
| __allocated_obj(__allocated_obj<_Alloc>&&) = default; |
| |
| // Default-initialize a value_type at *__ptr |
| __allocated_obj(__allocated_ptr<_Alloc>&& __ptr) |
| : __allocated_ptr<_Alloc>(std::move(__ptr)) |
| { ::new ((void*)this->get()) value_type; } |
| |
| // Call the destructor if an object is owned. |
| ~__allocated_obj() |
| { |
| if (static_cast<bool>(*this)) |
| this->get()->~value_type(); |
| } |
| |
| using __allocated_ptr<_Alloc>::operator=; |
| |
| value_type& operator*() const { return *this->get(); } |
| value_type* operator->() const { return this->get(); } |
| }; |
| |
| /// Construct an object in storage allocated using __a. |
| template<typename _Alloc> |
| inline __allocated_obj<_Alloc> |
| __allocate_guarded_obj(_Alloc& __a) |
| { |
| return { std::__allocate_guarded(__a) }; |
| } |
| |
| // An RAII type that acquires memory from an allocator. |
| // N.B. 'scoped' here in in the RAII sense, not the scoped allocator model, |
| // so this has nothing to do with `std::scoped_allocator_adaptor`. |
| // This class can be used to simplify the common pattern: |
| // |
| // auto ptr = alloc.allocate(1); |
| // try { |
| // std::construct_at(std::to_address(ptr), args); |
| // m_ptr = ptr; |
| // } catch (...) { |
| // alloc.deallocate(ptr, 1); |
| // throw; |
| // } |
| // |
| // Instead you can do: |
| // |
| // _Scoped_allocation sa(alloc); |
| // m_ptr = std::construct_at(sa.get(), args); |
| // (void) sa.release(); |
| // |
| // Or even simpler: |
| // |
| // _Scoped_allocation sa(alloc, std::in_place, args); |
| // m_ptr = sa.release(); |
| // |
| template<typename _Alloc> |
| struct _Scoped_allocation |
| { |
| using value_type = typename allocator_traits<_Alloc>::value_type; |
| using pointer = typename allocator_traits<_Alloc>::pointer; |
| |
| // Use `a` to allocate memory for `n` objects. |
| constexpr explicit |
| _Scoped_allocation(const _Alloc& __a, size_t __n = 1) |
| : _M_a(__a), _M_n(__n), _M_p(_M_a.allocate(__n)) |
| { } |
| |
| #if __glibcxx_optional >= 201606L |
| // Allocate memory for a single object and if that succeeds, |
| // construct an object using args. |
| // |
| // Does not do uses-allocator construction; don't use if you need that. |
| // |
| // CAUTION: the destructor will *not* destroy this object, it will only |
| // free the memory. That means the following pattern is unsafe: |
| // |
| // _Scoped_allocation sa(alloc, in_place, args); |
| // potentially_throwing_operations(); |
| // return sa.release(); |
| // |
| // If the middle operation throws, the object will not be destroyed. |
| template<typename... _Args> |
| constexpr explicit |
| _Scoped_allocation(const _Alloc& __a, in_place_t, _Args&&... __args) |
| : _Scoped_allocation(__a, 1) |
| { |
| // The target constructor has completed, so if the next line throws, |
| // the destructor will deallocate the memory. |
| allocator_traits<_Alloc>::construct(_M_a, get(), |
| std::forward<_Args>(__args)...); |
| } |
| #endif |
| |
| _GLIBCXX20_CONSTEXPR |
| ~_Scoped_allocation() |
| { |
| if (_M_p) [[__unlikely__]] |
| _M_a.deallocate(_M_p, _M_n); |
| } |
| |
| _Scoped_allocation(_Scoped_allocation&&) = delete; |
| |
| constexpr _Alloc |
| get_allocator() const noexcept { return _M_a; } |
| |
| constexpr value_type* |
| get() const noexcept |
| { return std::__to_address(_M_p); } |
| |
| [[__nodiscard__]] |
| constexpr pointer |
| release() noexcept { return std::__exchange(_M_p, nullptr); } |
| |
| private: |
| [[__no_unique_address__]] _Alloc _M_a; |
| size_t _M_n; |
| pointer _M_p; |
| }; |
| |
| #if __glibcxx_optional >= 201606L && __cpp_deduction_guides >= 201606L |
| template<typename _Alloc, typename... _Args> |
| _Scoped_allocation(_Alloc, in_place_t, _Args...) |
| -> _Scoped_allocation<_Alloc>; |
| #endif |
| |
| /// @endcond |
| _GLIBCXX_END_NAMESPACE_VERSION |
| } // namespace std |
| |
| #endif |
| #endif |