<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Allocator-aware library wrappers for dynamic allocation</title>
    <style type="text/css">
      html { margin: 0; padding: 0; color: black; background-color: white; }
      body { padding: 2em; font-size: medium; font-family: "DejaVu Serif", serif; line-height: 150%; }
      code { font-family: "DejaVu Sans Mono", monospace; color: #006; }

      h1, h2, h3 { margin: 1.5em 0 .75em 0; line-height: 125%; }

      div.code { white-space: pre-line; font-family: "DejaVu Sans Mono", monospace;
                 border: thin solid #E0E0E0; background-color: #F8F8F8; padding: 1em;
                 border-radius: 4px; }

      div.strictpre { white-space: pre; }

      table { border-collapse: collapse; margin: 2em auto; }

      th, td { text-align: left; vertical-align: top; padding: .5ex 1em; margin: 0; }

      td.new { background-color: #EFE; }
      td.new:after { content: "new!"; font-family: "DejaVu Sans", sans-serif; font-weight: bold; font-size: xx-small;
                     vertical-align: top; top: -1em; right: -1em; position: relative; float: right; color: #090; }

      thead th { border-top: 2px solid #333; border-bottom: 2px solid #333; }
      tbody tr:last-child th, tbody tr:last-child td { border-bottom: 2px solid #333; }

      .docinfo { float: right }
      .docinfo p { margin: 0; text-align:right; }
      .docinfo address { font-style: normal; }

      .quote { display: inline-block; clear: both; margin-left: 1ex;
                 border: thin solid #E0E0E0; background-color: #F8F8F8; padding: 1ex; }

      .modify { border-left: thick solid #999; border-right: thick solid #999; padding: 0 1em; }
      .insert { border-left: thick solid #0A0; border-right: thick solid #0A0; padding: 0 1em; }
      .insert h3, .insert h4, .insert p { text-decoration: underline; color: #0A0; }
      .comment { color: #456; }
      .inclassit { font-family: "DejaVu Serif", serif; font-style: italic; }
      .insinline { border-bottom: 2px solid #0A0; }

      ins { color: #090; }
      del { color: #A00; }
      ins code, del code, .insert code { color: inherit; }
    </style>
  </head>
  <body>
    <div class="docinfo">
      <p>ISO/IEC JTC1 SC22 WG21 P0211R3</p>
      <p>Date: 2020-01-14</p>
      <p>To: LEWG</p>
      <address>Thomas K&ouml;ppe &lt;<a href="mailto:tkoeppe@google.com">tkoeppe@google.com</a>&gt;</address>
    </div>

    <h1>Allocator-aware library wrappers for dynamic allocation</h1>

    <h2>Contents</h2>
    <!-- fgrep -e "<h2 id=" allocator_helpers_v3.html | sed -e 's/.*id="\(.*\)">\(.*\)<\/h2>/<li><a href="#\1">\2<\/a><\/li>/g' -->
    <ol>
      <li><a href="#history">Revision history</a></li>
      <li><a href="#summary">Summary</a></li>
      <li><a href="#examples">Example use cases</a></li>
      <li><a href="#spec1">Dynamic allocation wrappers</a></li>
      <li><a href="#spec2">Unique pointers</a></li>
      <li><a href="#questions">Design questions</a></li>
      <li><a href="#ack">Acknowledgements</a></li>
      <li><a href="#wording">Proposed wording</a></li>
    </ol>

    <h2 id="history">Revision history</h2>
    <ul>
      <li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0211r0.html">P0211R0</a>:
        Initial proposal, pre-Jacksonville 2016.</li>
      <li>D0211r1: make pointer in <code>allocate_delete</code> const; delete unnecessary null check
        from <code>allocate_new</code>; add some more design questions.</li>
      <li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0211r1.html">P0211R1</a>: Rename functions as requested by LEWG in Jacksonville 2016.
        Add conversions to <code>allocation_deleter</code>. Add formal wording.</li>
      <li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0211r2.html">P0211R2</a>:
        Remove <code>const</code> from a few places where it would not have been allowed.
        Use <code>std::to_address</code> (introduced by <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0653r2.html">P0653R2</a>).
        Add <code>noexcept</code> to <code>allocation_deleter</code> constructors. Allow rebinding. Wording tweaks.</li>
      <li>P0211R3 (this version): Discuss the absence of <code>_default_init</code> versions.
        Update <em>Remarks</em> to <em>Constraints</em>, and update section numbers.</li>
    </ul>

    <h2 id="summary">Summary</h2>

    <p><strong>Short form:</strong> We propose to add library functions that allow the
      systematic use of allocators as a customisation point for dynamic allocations. The new
      functions complete the following picture:</p>

    <table>
      <thead>
        <tr><th></th><th>using <code>operator {new,delete}</code></th><th>using allocator</th></tr>
      </thead>
      <tbody>
        <tr>
          <th rowspan="2">Manual</th>
          <td><code>T * p = new T(args...)</code></td>
          <td class="new"><code>auto p = allocator_new&lt;T&gt;(alloc, args...)</code></td>
        </tr>
        <tr>
          <td><code>delete p</code></td>
          <td class="new"><code>allocator_delete(alloc, p)</code></td>
        </tr>
        <tr>
          <th rowspan="2">Unique pointer</th>
          <td><code>default_delete&lt;T&gt;</code></td>
          <td class="new"><code>allocation_deleter&lt;T&gt;</code></td>
        </tr>
        <tr>
          <td><code>make_unique&lt;T&gt;(args...)</code></td>
          <td class="new"><code>allocate_unique&lt;T&gt;(alloc, args...)</code></td>
        </tr>
        <tr>
          <th>Shared pointer</th>
          <td><code>make_shared&lt;T&gt;(args...)</code></td>
          <td><code>allocate_shared&lt;T&gt;(alloc, args...)</code></td>
        </tr>
      </tbody>
    </table>

    <p><strong>Long form:</strong> The standard library rarely uses
      <code>new</code>/<code>delete</code> directly, but instead allows customisation of dynamic
      allocation and object construction via allocators. Currently this customisation is only
      available for container <em>elements</em> and for <code>shared_ptr</code> (as well as for
      a few other types that require dynamically allocated memory), but not for the top-level
      objects themselves.</p> <p>The proposal is to complete the library facilities for
      allocator-based customisation by providing a direct mechanism for creating and destroying
      a dynamically stored object through an allocator, as well as a new deleter type for
      <code>unique_ptr</code> to hold an allocator-managed unique pointee, together with the
      appropriate factory function.</p>

    <h2 id="examples">Example use cases</h2>

    <ul>
      <li>Consider an arena allocator. A vector may put its elements into the arena, but the
        vector itself cannot easily be placed in the same arena. The proposed
        <code>std::allocate_unique&lt;std::vector&lt;T,
        ScopedArenaAlloc&lt;T&gt;&gt;&gt;(arena_alloc)</code> allows this. (Here we assume the
        use of the usual alias idiom <code>template &lt;class T&gt; using ScopedArenaAlloc =
        std::scoped_allocator_adaptor&lt;ArenaAlloc&lt;T&gt;&gt;</code>;).</li> <li>Consider a
        shared memory allocator. As in the previous example, containers will put their elements
        in shared memory, but one process needs to create the container itself and place it in
        shared memory. This is now possible with <code>auto p =
        std::allocator_new&lt;std::vector&lt;T,
        ScopedShmemAlloc&lt;T&gt;&gt;&gt;(shmem_alloc)</code>. The returned pointer is
        presumably an offset pointer, and its offset value needs to be communicated to the other
        participating processes. The last process to use the container calls
        <code>allocator_delete(shmem_alloc, p)</code>.</li> <li>Allocator support is <a
        href="http://stackoverflow.com/a/23132307">frequently requested on Stack
        Overflow</a>.</li>
    </ul>

    <h2 id="spec1">Dynamic allocation wrappers</h2>

    <h3>Allocation and object creation</h3>

    <div class="code">template &lt;class T, class A, class ...Args&gt;
      auto allocator_new(A&amp; alloc, Args&amp;&amp;... args) {
      &nbsp;&nbsp;using TTraits = typeame allocator_traits&lt;A&gt;::template rebind_traits&lt;T&gt;;
      &nbsp;&nbsp;using TAlloc = typename allocator_traits&lt;A&gt;::template rebind_alloc&lt;T&gt;;

      &nbsp;&nbsp;auto a = TAlloc(alloc);
      &nbsp;&nbsp;auto p = TTraits::allocate(a, 1);

      &nbsp;&nbsp;try {
      &nbsp;&nbsp;&nbsp;&nbsp;TTraits::construct(a, to_address(p), std::forward&lt;Args&gt;(args)...);
      &nbsp;&nbsp;&nbsp;&nbsp;return p;
      &nbsp;&nbsp;} catch(...) {
      &nbsp;&nbsp;&nbsp;&nbsp;TTraits::deallocate(a, p, 1);
      &nbsp;&nbsp;&nbsp;&nbsp;throw;
      &nbsp;&nbsp;}
    }</div>

    <h3>Object destruction and Deallocation</h3>

    <div class="code">template &lt;class A, class P&gt;
      void allocator_delete(A&amp; alloc, P p) {
      &nbsp;&nbsp;using Elem = typename pointer_traits&lt;P&gt;::element_type;
      &nbsp;&nbsp;using Traits = typename allocator_traits&lt;A&gt;::template rebind_traits&lt;Elem&gt;;

      &nbsp;&nbsp;Traits::destroy(alloc, to_address(p));
      &nbsp;&nbsp;Traits::deallocate(alloc, p, 1);
    }</div>
          
    <h2 id="spec2">Unique pointers</h2>

    <p>To allow <code>std::unique_ptr</code> to use custom allocators, we first need a deleter template that stores the allocator:</p>

    <div class="code">template &lt;class A&gt;
      struct allocation_deleter {
      &nbsp;&nbsp;using pointer = typename allocator_traits&lt;A&gt;::pointer;

      &nbsp;&nbsp;A a_;&nbsp;&nbsp;// <em>exposition only</em>

      &nbsp;&nbsp;allocation_deleter(const A&amp; a) noexcept : a_(a) {}

      &nbsp;&nbsp;void operator()(pointer p) {
      &nbsp;&nbsp;&nbsp;&nbsp;allocator_delete(a_, p);
      &nbsp;&nbsp;}
      };
    </div>

    <p>The factory function is:</p>

    <div class="code">template &lt;class T, class A, class ...Args&gt;
      auto allocate_unique(A&amp; alloc, Args&amp;&amp;... args) {
      &nbsp;&nbsp;using TAlloc = typename allocator_traits&lt;A&gt;::rebind_alloc&lt;T&gt;;
      &nbsp;&nbsp;return unique_ptr&lt;T, allocation_deleter&lt;TAlloc&gt;&gt;(allocator_new&lt;T&gt;(alloc, std::forward&lt;Args&gt;(args)...), alloc);
      }</div>

    <p>The type <code>T</code> must not be an array type. The pathological case where the
      template argument of <code>allocation_deleter&lt;A&gt;</code> is equal (up to qualification)
      to <code>allocation_deleter&lt;A&gt;</code> has to be taken into account for the purpose of
      the constructor.</p>

    <h2 id="questions">Design questions</h2>

    <h3>Naming</h3>

    <p>The name for the deleter class: &ldquo;allocation_deleter&rdquo; or &ldquo;allocation_delete&rdquo;?
      (We had previously used &ldquo;allocate_delete&rdquo; for both the function and the class,
      but that would have been very confusing.)</p>

    <h3>Default initialization</h3>

    <p>In C++20, the creation functions <code>make_unique</code>, <code>make_shared</code>,
      and <code>allocate_shared</code> were augmented by <code>make_unique_default_init</code>,
      <code>make_shared_default_init</code>, and <code>allocate_shared_default_init</code>.
      Whereas the former (called with no arguments) value-initialize an object (e.g. <code>::new T()</code>),
      the latter default-initialize instead (e.g. <code>::new T</code>).</p>

    <p>One might ask whether there should not also be analogous default-initializing
      versions of the allocation helpers that are proposed here. However, this is a false
      analogy, and the answer is no: The proposed allocation helpers are meant to be a
      symmetric pair of allocation/construction and destruction/deallocation. Note that
      <code>allocator_delete</code> and <code>allocation_deleter::operator()</code> both
      call the allocator&rsquo;s <code>destroy</code> function, which therefore must be
      matched with initialization by <code>construct</code>. This in turn precludes any
      notion of default-initialization, which the allocator API does not allow.</p>
    
    <p>By contrast, <code>make_unique</code> and <code>make_shared</code> and their
      default-initializing versions do not use allocators
      and can thus correctly call <code>delete</code> or <code>delete[]</code> regardless of
      how the initialization was performed, and <code>allocate_shared</code>/<code>allocate_shared_default_init</code>
      use different dynamic deleters so that the allocator is used for the former, and raw
      <code>delete</code> is used for the latter.</p>

    <h2 id="ack">Acknowledgements</h2>

    <p>Many thanks to Daniel Kr&uuml;gler for a thorough review and invaluable corrections
      of the first version from 2016, to the members of LEWG who reviewed the draft and
      provided many valuable improvements in 2018, and to Alisdair Meredith for pointing
      out default-initialization.</p>

    <h2 id="wording">Proposed wording</h2>

    <p>In [memory.syn, 20.10.2], add to the synopsis:</p>

    <div class="modify">
      <div class="code">// <span class="inclassit">20.10.8, uses_allocator</span>
        template &lt;class T, class Alloc&gt; struct uses_allocator;

        [&hellip;]
        
        // <span class="inclassit">20.10.9, allocator traits</span>
        template &lt;class Alloc&gt; struct allocator_traits;

        <ins>// <span class="inclassit">20.10.?, allocation helpers:</span>
        template &lt;class A, class ...Args&gt;
        typename allocator_traits&lt;A&gt;::pointer allocator_new(A&amp; alloc, Args&amp;&amp;... args);
        template &lt;class A&gt;
        void allocator_delete(A&amp; alloc, typename allocator_traits&lt;A&gt;::pointer p);</ins>

        // <span class="inclassit">20.10.<del>10</del><ins>?</ins>, the default allocator:</span>
        template &lt;class T&gt; class allocator;
        template &lt;&gt; class allocator&lt;void&gt;;
        template &lt;class T, class U&gt;
        bool operator==(const allocator&lt;T&gt;&amp;, const allocator&lt;U&gt;&amp;) noexcept;
        template &lt;class T, class U&gt;
        bool operator!=(const allocator&lt;T&gt;&amp;, const allocator&lt;U&gt;&amp;) noexcept;
      </div>
    </div>

    <p></p>

    <div class="modify">
      <div class="code">// <span class="inclassit">20.11.1 class template unique_ptr:</span>
        template &lt;class T&gt; struct default_delete;
        template &lt;class T&gt; struct default_delete&lt;T[]&gt;;
        <ins>template &lt;class A&gt; struct allocation_deleter;</ins>
        template &lt;class T, class D = default_delete&lt;T&gt;&gt; class unique_ptr;
        template &lt;class T, class D&gt; class unique_ptr&lt;T[], D&gt;;

        template &lt;class T, class... Args&gt; unique_ptr&lt;T&gt; make_unique(Args&amp;&amp;... args);
        template &lt;class T&gt; unique_ptr&lt;T&gt; make_unique(size_t n);
        template &lt;class T, class... Args&gt; unspecified make_unique(Args&amp;&amp;...) = delete;
        <ins>template &lt;class T, class A, class ...Args&gt; unique_ptr&lt;T, allocation_deleter&lt;A&gt;&gt; allocate_unique(A&amp; alloc, Args&amp;&amp;... args)</ins>
      </div>
    </div>

    <p>Insert a new subsection between 20.9.9 (allocator traits) and 20.9.10 (the default allocator):</p>

    <div class="insert">
      <h3>20.10.? Allocation helpers [allocator.alloc_helpers]</h3>

      <p>The function templates <code>allocator_new</code> and <code>allocator_delete</code>
        create and delete objects dynamically using an allocator to provide storage and perform
        construction (rather than by looking up an allocation function ([basic.stc.dynamic.allocation, 6.6.4.4.1])).</p>

      <div class="code"><ins>template &lt;class T, class A, class ...Args&gt;</ins>
        &nbsp; <ins>typename allocator_traits&lt;A&gt;::pointer allocator_new(A&amp; alloc, Args&amp;&amp;... args);</ins></div>

      <p><em>Requires</em>: <code>A</code> shall satisfy the <em>Cpp17Allocator</em> requirements ([allocator.requirements, 16.5.3.5]).</p>

      <p><em>Effects</em>: Let <code>TTraits</code> be the type <code>allocator_traits&lt;A&gt;::rebind_traits&lt;T&gt;</code>.
        Obtains storage for one element from a copy <code>a</code>
        of <code>alloc</code> rebound for type <code>T</code> by
        calling <code>TTraits::allocate(a, 1)</code> and constructs an object by calling
        <code>TTraits::construct(a, to_address(p), std::forward&lt;Args&gt;(args)...)</code>,
        where <code>p</code> is the pointer obtained from the allocation. If the construction exits with an exception, the storage
        is released using <code>TTraits::deallocate(a, p, 1)</code>.</p>

      <p><em>Returns</em>: A pointer to the obtained storage that holds the constructed object.
        [<em>Note</em>: This pointer may have a user-defined type. &ndash; <em>end note</em>]</p>

      <p><em>Throws</em>: Any exception thrown by <code>TTraits::allocate</code> or by <code>TTraits::construct</code>.</p>

      <div class="code"><ins>template &lt;class A, class P&gt;</ins>
        &nbsp; <ins>void allocator_delete(A&amp; alloc, P p);</ins></div>

      <p><em>Requires</em>: <code>A</code> shall satisfy the <em>Cpp17Allocator</em> requirements ([allocator.requirements, 16.5.3.5]).
        <code>P</code> shall satisfy the <em>Cpp17NullablePointer</em> requirements ([nullablepointer.requirements, 16.5.3.3]).
        The pointer <code>p</code> was obtained from an allocator that compares
        equal to <code>alloc</code>, and <code>p</code> is dereferenceable.</p>

      <p><em>Effects</em>: Let <code>TTraits</code> be the type <code>allocator_traits&lt;A&gt;::rebind_traits&lt;pointer_traits&lt;P&gt;::element_type&gt;</code>.
        Uses a copy <code>a</code> of <code>alloc</code> rebound to
        the <code>TTraits::value_type</code> to destroy the object <code>*p</code>
        by calling <code>TTraits::destroy(a, to_address(p))</code>
        and to release the underlying storage by calling <code>TTraits::deallocate(a, p, 1)</code>.</p>
    </div>

    <p>Insert a new subsection 20.11.1.1.? after 20.11.1.1.3 (<code>default_delete&lt;T[]&gt;</code>):</p>

    <div class="insert">
      <h4>20.11.1.1.? <code>allocation_deleter&lt;A&gt;</code> [unique.ptr.dltr.alloc]</h4>

      <div class="code"><ins>template &lt;class A&gt;
        struct allocation_deleter {</ins>
        &nbsp;&nbsp;<ins>using pointer = typename allocator_traits&lt;A&gt;::pointer;</ins>

        &nbsp;&nbsp;<ins>allocation_deleter(const A&amp; alloc) noexcept;</ins>

        &nbsp;&nbsp;<ins>template &lt;class B&gt;</ins>
        &nbsp;&nbsp;<ins>allocation_deleter(const allocation_deleter&lt;B&gt;&amp; other) noexcept;</ins>

        &nbsp;&nbsp;<ins>void operator()(pointer p);</ins>

        <ins>private:</ins>
        &nbsp;&nbsp;<ins>[[no_unique_address]] A a_;&nbsp;&nbsp;// <em>exposition only</em></ins>
        <ins>};</ins>
      </div>

      <p></p>
      
      <div class="code"><ins>allocation_deleter(const A&amp; alloc) noexcept;</ins></div>
      <p><em>Effects</em>: Initializes <code>a_</code> with <code>alloc</code>.</p>
      <p><em>Constraints</em>: <code>A</code>, ignoring qualifications, is not <code>allocation_deleter&lt;A&gt;</code>.</p>

      <div class="code"><ins>template &lt;class B&gt;</ins>
        &nbsp; <ins>allocation_deleter(const allocation_deleter&lt;B&gt;&amp; other) noexcept;</ins></div>
      <p><em>Effects</em>: Initializes <code>a_</code> with <code>other.a_</code>.</p>
      <p><em>Constraints</em>: <code>typename allocator_traits&lt;B&gt;::pointer</code> is implicitly convertible to <code>pointer</code>.</p>

      <div class="code"><ins>void operator()(pointer p);</ins></div>
      <p><em>Effects</em>: Calls <code>allocator_delete(a_, p)</code>.</p>
    </div>

    <p>Append a new paragraph to the end of subsection 20.11.1.4 (<code>unique_ptr</code> creation):</p>

    <div class="insert">
      <div class="code"><ins>template &lt;class T, class A&gt;</ins>
        &nbsp; <ins>unique_ptr&lt;T, allocation_deleter&lt;allocator_traits&lt;A&gt;::rebind_alloc&lt;T&gt;&gt;&gt; allocate_unique_default_init(A&amp; alloc)</ins></div>

      <p><em>Constraints</em>: <code>T</code> is not an array.</p>

      <p><em>Returns</em>: <code>unique_ptr&lt;T, allocation_deleter&lt;allocator_traits&lt;A&gt;::rebind_alloc&lt;T&gt;&gt;&gt;(allocator_new&lt;T&gt;(alloc, std::forward&lt;Args&gt;(args)...), alloc)</code>.

      <p><em>Throws</em>: Any exception thrown by <code>allocator_new&lt;T&gt;</code>.</p>
    </div>

  </body>
</html>
