On Thu, May 22, 2025 at 12:21 PM Daniel Krügler <daniel.krueg...@gmail.com> wrote:
> > > Am Do., 22. Mai 2025 um 11:41 Uhr schrieb Tomasz Kamiński < > tkami...@redhat.com>: > >> From: Jonathan Wakely <jwak...@redhat.com> >> >> libstdc++-v3/ChangeLog: >> >> * include/bits/allocated_ptr.h (_Scoped_allocation): New class >> template. >> >> Co-Authored-By: Tomasz Kamiński <tkami...@redhat.com> >> Signed-off-by: Tomasz Kamiński <tkami...@redhat.com> >> --- >> Tested on x86_64-linux. OK for trunk? >> >> libstdc++-v3/include/bits/allocated_ptr.h | 96 +++++++++++++++++++++++ >> 1 file changed, 96 insertions(+) >> >> diff --git a/libstdc++-v3/include/bits/allocated_ptr.h >> b/libstdc++-v3/include/bits/allocated_ptr.h >> index 0b2b6fe5820..aa5355f0e2f 100644 >> --- a/libstdc++-v3/include/bits/allocated_ptr.h >> +++ b/libstdc++-v3/include/bits/allocated_ptr.h >> @@ -36,6 +36,7 @@ >> # include <type_traits> >> # include <bits/ptr_traits.h> >> # include <bits/alloc_traits.h> >> +# include <bits/utility.h> >> >> namespace std _GLIBCXX_VISIBILITY(default) >> { >> @@ -136,6 +137,101 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> 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); >> > > Why is the situation *unlikely* that _M_p has a non-nullptr content? > Shouldn't that actually be likely? > This object manages allocation of raw memory, and not contained objects. So the non-exceptional code path looks like: _Scoped_allocation a(alloc, in_place, argos); _M_ptr = a.release(); And release set the _M_p to nullptr. In other words, this if is taken only in case of exception. > > - Daniel > > >> + } >> + >> + _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 >> -- >> 2.49.0 >> >>