220 40640 <5f11127d-1ab8-41d8-8b8d-8e9781770f20@isocpp.org> article
Path: news.gmane.org!.POSTED!not-for-mail
From: Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
Newsgroups: gmane.comp.lang.c++.isocpp.proposals
Subject: Re: Inferring stateless deleter from a partially
 stateful allocator
Date: Fri, 19 Oct 2018 08:40:01 -0700 (PDT)
Lines: 267
Approved: news@gmane.org
Message-ID: <5f11127d-1ab8-41d8-8b8d-8e9781770f20@isocpp.org>
References: <CANgTfEhMmPmhgK2B1Lw4vJCDN9fUJ9mUWaL_mDNx6OBLXCWN7w@mail.gmail.com>
Reply-To: std-proposals@isocpp.org
NNTP-Posting-Host: blaine.gmane.org
Mime-Version: 1.0
Content-Type: multipart/mixed; 
	boundary="----=_Part_3845_751350406.1539963601340"
X-Trace: blaine.gmane.org 1539963478 26485 195.159.176.226 (19 Oct 2018 15:37:58 GMT)
X-Complaints-To: usenet@blaine.gmane.org
NNTP-Posting-Date: Fri, 19 Oct 2018 15:37:58 +0000 (UTC)
To: ISO C++ Standard - Future Proposals <std-proposals@isocpp.org>
Original-X-From: std-proposals+bncBDLZJYWNDQINF5NH3YCRUBFQLROAQ@isocpp.org Fri Oct 19 17:37:54 2018
Return-path: <std-proposals+bncBDLZJYWNDQINF5NH3YCRUBFQLROAQ@isocpp.org>
Envelope-to: gclcip-std-proposals@m.gmane.org
Original-Received: from mail-yw1-f70.google.com ([209.85.161.70])
	by blaine.gmane.org with esmtp (Exim 4.84_2)
	(envelope-from <std-proposals+bncBDLZJYWNDQINF5NH3YCRUBFQLROAQ@isocpp.org>)
	id 1gDWqX-0006n7-HU
	for gclcip-std-proposals@m.gmane.org; Fri, 19 Oct 2018 17:37:53 +0200
Original-Received: by mail-yw1-f70.google.com with SMTP id 131-v6sf21820242ywe.1
        for <gclcip-std-proposals@m.gmane.org>; Fri, 19 Oct 2018 08:40:04 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=isocpp-org.20150623.gappssmtp.com; s=20150623;
        h=date:from:to:message-id:in-reply-to:references:subject:mime-version
         :x-original-sender:reply-to:precedence:mailing-list:list-id
         :list-post:list-help:list-archive:list-subscribe:list-unsubscribe;
        bh=SOrv4Qfy8lSB/FxGbtxjtm9xkeB9lExmDzc8BmzUFHw=;
        b=a23/8PN+mQzeHFchI9mwv4AnC3c0/DErQh1NjpWum/VlcM6uYvadpqeC87nC9E0K9r
         sL+VwrrBzdX0yoVFh5X63DOiNSr8yE23rtD2Ov4v9O688ktN221b3HLvL/5WO6pNn90S
         uWHPCPXCrf4Sd8Uil8XzRY8nuXwtklQ3cODnLvKfNeRbVWoN7pqnVnbyBgUwhQgT5vXw
         YXOV7WEIjA5lX8QpOkRVyUGcfosDHSGepz0Kl8FcB/mbYYsX0D3iwcRSh0+1hndqnrGD
         QIvQkRKesjPfQ6OFJs7jx0LGH5xm/a4mnE8Y724uXTC34Jep+zWQytUN7rIRZEmNXnxw
         c9lA==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=20161025;
        h=date:from:to:message-id:in-reply-to:references:subject:mime-version
         :x-original-sender:reply-to:precedence:mailing-list:list-id
         :list-post:list-help:list-archive:list-subscribe:list-unsubscribe;
        bh=SOrv4Qfy8lSB/FxGbtxjtm9xkeB9lExmDzc8BmzUFHw=;
        b=klsGzAWoPXGJfJLX67fH1RcD1oOJnJxj3pZ7lb49MQW7xV8il3LnUfG4YqWRmecyAV
         9uQzHSXEXRGTpCUu673CbcHx9JM2Fi4KfN2nPKnqo/UB15bPCl7G4hKXUBxPEZfxIdns
         49S9rky5GbajQamc5kM2/ieDkBPsLMWGulgBWuMJfd2/EOkNZ6lvC8LAjZw+y13pgIer
         ZK8k4mYXl7KCRjcZFpsRoO5w/myaDJGQBfg1VvkKGsZKDS1UnfJ1b1h0eTdyT1UhscVz
         +BoomJ4si4ouRWGCt5qiPIDFhR00vT4XyN5dPc6uHbkroAkKvwOh4TubNk6qsN3Jbskk
         NVOg==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20161025;
        h=x-gm-message-state:date:from:to:message-id:in-reply-to:references
         :subject:mime-version:x-original-sender:reply-to:precedence
         :mailing-list:list-id:x-spam-checked-in-group:list-post:list-help
         :list-archive:list-subscribe:list-unsubscribe;
        bh=SOrv4Qfy8lSB/FxGbtxjtm9xkeB9lExmDzc8BmzUFHw=;
        b=VrblJ6CJnDNpRCIvK2ECcVWY+ZCW6GLrn9E81SH/PYKlTD2flr0ZtrHqCRGnfLGEd3
         0hHGVFbgj0lR7XerajQJfnSgT65UgXxYJfZ0xnOBNrN/CzH7jOCoLk5yDf7tffnFMWay
         S6cwShd2u+Q0QX3qtmi9XheaBGaSbNkXu/xkWptMPCfRo9n4SGsbxW0HD4kldFSL0MzZ
         2L+98x6k9xSFR0K7LTDhWpnXeWq9PVhMa8tr12DKzkti62ek/agwyZMgZq/PwrOru1Tb
         su09OwfOLhMJ06mxrTBuh2vWT+I7qiKvKHmFrh/fVlx5EjfpR+bAe+iTq7QsdArsY/SW
         /lyQ==
X-Gm-Message-State: ABuFfoiSXWU2mkypcbbtdy8iqQVtx7BXuxnd5CsfIBSFcjDuklK+mmBC
	c/81ne/j+e3S0KBKvSyIFw+XgA==
X-Google-Smtp-Source: ACcGV62lPp1CtZeE3SQdgzhcWeZ7/rUED8mfTm/C8BFX/jycTkINtifdJqwa92b3I73/iIOgED+9ig==
X-Received: by 2002:a25:844c:: with SMTP id r12-v6mr20319079ybm.35.1539963603862;
        Fri, 19 Oct 2018 08:40:03 -0700 (PDT)
X-BeenThere: std-proposals@isocpp.org
Original-Received: by 2002:a25:f30c:: with SMTP id c12-v6ls539507ybs.2.gmail; Fri, 19
 Oct 2018 08:40:02 -0700 (PDT)
X-Received: by 2002:a25:50cd:: with SMTP id e196-v6mr22400ybb.0.1539963602110;
        Fri, 19 Oct 2018 08:40:02 -0700 (PDT)
In-Reply-To: <CANgTfEhMmPmhgK2B1Lw4vJCDN9fUJ9mUWaL_mDNx6OBLXCWN7w@mail.gmail.com>
X-Original-Sender: arthur.j.odwyer@gmail.com
Precedence: list
Mailing-list: list std-proposals@isocpp.org; contact std-proposals+owners@isocpp.org
List-ID: <std-proposals.isocpp.org>
X-Spam-Checked-In-Group: std-proposals@isocpp.org
X-Google-Group-Id: 399137483710
List-Post: <https://groups.google.com/a/isocpp.org/group/std-proposals/post>, <mailto:std-proposals@isocpp.org>
List-Help: <https://support.google.com/a/isocpp.org/bin/topic.py?topic=25838>, <mailto:std-proposals+help@isocpp.org>
List-Archive: <https://groups.google.com/a/isocpp.org/group/std-proposals/>
List-Subscribe: <https://groups.google.com/a/isocpp.org/group/std-proposals/subscribe>,
 <mailto:std-proposals+subscribe@isocpp.org>
List-Unsubscribe: <mailto:googlegroups-manage+399137483710+unsubscribe@googlegroups.com>,
 <https://groups.google.com/a/isocpp.org/group/std-proposals/subscribe>
Xref: news.gmane.org gmane.comp.lang.c++.isocpp.proposals:40640
Archived-At: <http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/40640>

------=_Part_3845_751350406.1539963601340
Content-Type: multipart/alternative; 
	boundary="----=_Part_3846_544746132.1539963601341"

------=_Part_3846_544746132.1539963601341
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

On Wednesday, October 17, 2018 at 7:17:36 AM UTC-7, Gareth Lloyd wrote:
>
> tl;dr; Currently a deleter is a facade over an allocator which currently=
=20
> doesn't hide how big the allocator is.
>
> Allocators provides a customization point for a generic data structure=20
> which may allocate internal structures.=20
>
> An allocator controls allocation, construction, destruction and=20
> deallocation. In the standard we have some opinionated allocators=20
> std::allocator, std::scoped_allocator_adaptor and=20
> std::pmr::polymorphic_allocator.
>
> With current allocators, if any one of those customization points require=
=20
> state then the entire allocator is stateful. This is fine if you are usin=
g=20
> the allocator as an allocator, you are going to be using those methods th=
at=20
> require state so the state is not an overhead. But there are situations,=
=20
> like deleter, where you are only using a subset of those methods.
>
> For a concrete example, allocate_unique needs the allocator to do the=20
> allocate and construct. But the returned unique_ptr only requires a=20
> specialized deleter based on the provided allocator. This deleter is a=20
> facade of the allocator, simplifying the interface and only using destroy=
=20
> and deallocate methods of the allocator. If those methods are stateless i=
n=20
> nature then the unique_ptr should not have to carry unnecessarily state=
=20
> that the deleter doesn't use.=20
>
> For allocators which are partially stateful there is currently no facilit=
y=20
> to make an allocate_unique implementation which can sense if the deleter=
=20
> methods (destroy and deallocate) are stateless. Without that sense the=20
> deleter has to be as stateful as the allocator it was derived from.
>

IIUC and AFAICT, the only situation where this comes up in practice is with=
=20
memory resources (heaps) where `deallocate` is a no-op.

In practice `destroy` is always "stateless" because its behavior depends=20
only on the type T being destroyed, not on the identity of the allocator=20
doing the destruction. (In fact, I think Mark Zeren is working on a=20
proposal to deprecate `destroy`, although I don't see it in the pre=E2=80=
=93San=20
Diego mailing.)
In practice, `deallocate` is frequently "stateless," e.g. when the=20
allocator is std::allocator<T>; but in most cases `deallocate` is stateless=
=20
*because* the allocator is stateless (i.e. the allocator has only one=20
possible value), in which case your optimization doesn't buy us anything=20
because the allocator is already an empty class. So the usefulness is=20
limited (in practice, AFAICT) to the case where the allocator is stateful=
=20
(or, as I like to say <https://youtu.be/0MdSJsCTRkY>, "value-ful") and=20
where `deallocate` is a no-op.

`deallocate` is not a no-op for any standard-library-provided allocator=20
type (std::allocator, std::pmr::polymorphic_allocator,=20
std::scoped_allocator_adaptor).
It could conceivably be a no-op for a user-provided allocator type A<T>=20
where A<T>'s value was restricted to point to a memory resource with a=20
no-op `deallocate`. For example:

template<class T>
struct monotonic_buffer_allocator : public=20
std::pmr::polymorphic_allocator<T> {
    using Base =3D std::pmr::polymorphic_allocator<T>;

    monotonic_buffer_allocator(std::pmr::monotonic_buffer_resource *mr) :=
=20
mr_(mr) {}
    template<class U> monotonic_buffer_allocator(const=20
monotonic_buffer_allocator<U>& rhs) : Base(rhs) {}

    using Base::allocate;
    using Base::construct;
    using Base::destroy;
    void deallocate(T *, size_t) {}

    template<class U> struct rebind { using other =3D=20
monotonic_buffer_allocator<U>; }
};

`monotonic_buffer_allocator` here is "value-ful" (non-empty) but also has a=
=20
no-op `deallocate` and a stateless `destroy`; so conceivably=20
`allocate_unique` could be written to take advantage of this property.

I would spell it `std::has_trivial_deallocate_v<A<T>>`.
Incidentally, I don't like your `stateless_deleter_v<A<T>>` because it is=
=20
missing an `is_` on the front.

Furthermore, observe that when `has_trivial_deallocate_v<A<T>>`, then the=
=20
`unique_ptr` returned from `allocate_unique<T, A<T>>` can be *trivially=20
destructible*. This seems like a very nice property to have,=20
philosophically speaking, even though I can't off the top of my head come=
=20
up with any concrete application for it.

A compromise approach might be to permit the allocator to specify a member=
=20
typedef `using deleter =3D ...` (defaulted to `allocator_delete<A<T>>` in=
=20
`allocator_traits`) which could be customized by the designer of the=20
allocator type. Then this deleter could be stateless and/or have a trivial=
=20
`deallocate` method.

Bottom lines:
- I think the property of *trivial* deallocate is much more directly=20
relevant to your use-case than is the property of *stateless* deallocate.
- I think it is not worth asking WG21 to pursue the *detection and=20
exploitation* of either property, until there *exists* some standard=20
allocator having the property, which is not likely ever to happen. It's=20
easy to hack up the existence and detection mechanisms within your own=20
codebase, as you've done, and then provide `my::allocate_unique` to exploit=
=20
them. You don't need to be friends with `std::unique_ptr` to make that=20
happen; you just need to provide a custom deleter type, which is already=20
possible in standard C++.

=E2=80=93Arthur

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/5f11127d-1ab8-41d8-8b8d-8e9781770f20%40isocpp.or=
g.

------=_Part_3846_544746132.1539963601341
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">On Wednesday, October 17, 2018 at 7:17:36 AM UTC-7, Gareth=
 Lloyd wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-le=
ft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">=
<div class=3D"gmail_quote"><div dir=3D"ltr"><div dir=3D"ltr"><div dir=3D"lt=
r"><div dir=3D"ltr"><div dir=3D"ltr"><div dir=3D"ltr"><div dir=3D"ltr"><div=
 dir=3D"ltr"><div dir=3D"ltr">tl;dr; Currently a deleter is a facade over a=
n allocator which currently doesn&#39;t hide how big the allocator is.</div=
><div dir=3D"ltr"><br></div><div dir=3D"ltr">Allocators provides a customiz=
ation point for a generic data structure which may allocate internal struct=
ures. <br></div><div dir=3D"ltr"><br></div><div dir=3D"ltr">An
 allocator controls allocation, construction, destruction and=20
deallocation. In the standard we have some opinionated allocators=20
std::allocator, std::scoped_allocator_adaptor and std::pmr::polymorphic_<wb=
r>allocator.</div><div dir=3D"ltr"><br></div><div>With
 current allocators, if any one of those customization points require=20
state then the entire allocator is stateful. This is fine if you are=20
using the allocator as an allocator, you are going to be using those=20
methods that require state so the state is not an overhead. But there=20
are situations, like deleter, where you are only using a subset of those
 methods.</div><div><br></div><div>For a concrete example,=20
allocate_unique needs the allocator to do the allocate and construct.=20
But the returned unique_ptr only requires a specialized deleter based on
 the provided allocator. This deleter is a facade of the allocator,=20
simplifying the interface and only using destroy and deallocate methods=20
of the allocator. If those methods are stateless in nature then the=20
unique_ptr should not have to carry unnecessarily state that the deleter
 doesn&#39;t use. <br></div><div><br></div>For allocators which are partial=
ly stateful there is currently no=20
facility to make an allocate_unique implementation=20
which can sense if the deleter methods (destroy and deallocate) are=20
stateless. Without that sense the deleter has to be as stateful as the=20
allocator it was derived from.</div></div></div></div></div></div></div></d=
iv></div></div></blockquote><div><br></div><div>IIUC and AFAICT, the only s=
ituation where this comes up in practice is with memory resources (heaps) w=
here `deallocate` is a no-op.</div><div><br></div><div>In practice `destroy=
` is always &quot;stateless&quot; because its behavior depends only on the =
type T being destroyed, not on the identity of the allocator doing the dest=
ruction. (In fact, I think Mark Zeren is working on a proposal to deprecate=
 `destroy`, although I don&#39;t see it in the pre=E2=80=93San Diego mailin=
g.)</div><div>In practice, `deallocate` is frequently &quot;stateless,&quot=
; e.g. when the allocator is std::allocator&lt;T&gt;; but in most cases `de=
allocate` is stateless <i>because</i> the allocator is stateless (i.e. the =
allocator has only one possible value), in which case your optimization doe=
sn&#39;t buy us anything because the allocator is already an empty class. S=
o the usefulness is limited (in practice, AFAICT) to the case where the all=
ocator is stateful (or, <a href=3D"https://youtu.be/0MdSJsCTRkY">as I like =
to say</a>, &quot;value-ful&quot;) and where `deallocate` is a no-op.</div>=
<div><br></div><div>`deallocate` is not a no-op for any standard-library-pr=
ovided allocator type (std::allocator, std::pmr::polymorphic_allocator, std=
::scoped_allocator_adaptor).</div><div>It could conceivably be a no-op for =
a user-provided allocator type A&lt;T&gt; where A&lt;T&gt;&#39;s value was =
restricted to point to a memory resource with a no-op `deallocate`. For exa=
mple:</div><div><br></div><div>template&lt;class T&gt;</div><div>struct mon=
otonic_buffer_allocator : public std::pmr::polymorphic_allocator&lt;T&gt; {=
</div><div>=C2=A0 =C2=A0 using Base =3D std::pmr::polymorphic_allocator&lt;=
T&gt;;</div><div><br></div><div>=C2=A0 =C2=A0 monotonic_buffer_allocator(st=
d::pmr::monotonic_buffer_resource *mr) : mr_(mr) {}<br></div><div><div>=C2=
=A0 =C2=A0 template&lt;class U&gt; monotonic_buffer_allocator(const monoton=
ic_buffer_allocator&lt;U&gt;&amp; rhs) : Base(rhs) {}</div></div><div><br><=
/div><div>=C2=A0 =C2=A0 using Base::allocate;</div><div>=C2=A0 =C2=A0 using=
 Base::construct;</div><div>=C2=A0 =C2=A0 using Base::destroy;</div><div>=
=C2=A0 =C2=A0 void deallocate(T *, size_t) {}</div><div><br></div><div>=C2=
=A0 =C2=A0 template&lt;class U&gt; struct rebind { using other =3D monotoni=
c_buffer_allocator&lt;U&gt;; }</div><div>};<br></div><div><br></div><div>`m=
onotonic_buffer_allocator` here is &quot;value-ful&quot; (non-empty) but al=
so has a no-op `deallocate` and a stateless `destroy`; so conceivably `allo=
cate_unique` could be written to take advantage of this property.</div><div=
><br></div><div>I would spell it `std::has_trivial_deallocate_v&lt;A&lt;T&g=
t;&gt;`.</div><div>Incidentally, I don&#39;t like your `stateless_deleter_v=
&lt;A&lt;T&gt;&gt;` because it is missing an `is_` on the front.</div><div>=
<br></div><div>Furthermore, observe that when `has_trivial_deallocate_v&lt;=
A&lt;T&gt;&gt;`, then the `unique_ptr` returned from `allocate_unique&lt;T,=
 A&lt;T&gt;&gt;` can be <i><b>trivially destructible</b></i>. This seems li=
ke a very nice property to have, philosophically speaking, even though I ca=
n&#39;t off the top of my head come up with any concrete application for it=
..</div><div><br></div><div>A compromise approach might be to permit the all=
ocator to specify a member typedef `using deleter =3D ...` (defaulted to `a=
llocator_delete&lt;A&lt;T&gt;&gt;` in `allocator_traits`) which could be cu=
stomized by the designer of the allocator type. Then this deleter could be =
stateless and/or have a trivial `deallocate` method.</div><div><br></div><d=
iv>Bottom lines:</div><div>- I think the property of <i><b>trivial</b></i> =
deallocate is much more directly relevant to your use-case than is the prop=
erty of=C2=A0<i><b>stateless</b></i> deallocate.</div><div>- I think it is =
not worth asking WG21 to pursue the <i><b>detection and exploitation</b></i=
> of either property, until there <i><b>exists</b></i> some standard alloca=
tor having the property, which is not likely ever to happen. It&#39;s easy =
to hack up the existence and detection mechanisms within your own codebase,=
 as you&#39;ve done, and then provide `my::allocate_unique` to exploit them=
.. You don&#39;t need to be friends with `std::unique_ptr` to make that happ=
en; you just need to provide a custom deleter type, which is already possib=
le in standard C++.</div><div><br></div><div>=E2=80=93Arthur</div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/5f11127d-1ab8-41d8-8b8d-8e9781770f20%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/5f11127d-1ab8-41d8-8b8d-8e9781770f20=
%40isocpp.org</a>.<br />

------=_Part_3846_544746132.1539963601341--

------=_Part_3845_751350406.1539963601340--

.
