Am Do., 22. Mai 2025 um 12:25 Uhr schrieb Tomasz Kaminski <
tkami...@redhat.com>:

>
>
> 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.
>

Got it - thanks!

- Daniel

Reply via email to