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);
+      }
+
+      _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

Reply via email to