// 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
// .
/** @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
#else
# include
# include
# include
# include
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/// @cond undocumented
/// Non-standard RAII type for managing pointers obtained from allocators.
template
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>>
__allocated_ptr(_Alloc& __a, _Ptr __ptr)
: _M_alloc(std::__addressof(__a)),
_M_ptr(pointer_traits::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
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
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(*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
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
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
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
_Scoped_allocation(_Alloc, in_place_t, _Args...)
-> _Scoped_allocation<_Alloc>;
#endif
/// @endcond
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif
#endif