220 32775 <b8fa33b0-81ab-4e87-bf9b-0372a4219e26@isocpp.org> article
Path: news.gmane.org!.POSTED!not-for-mail
From: Nicol Bolas <jmckesson@gmail.com>
Newsgroups: gmane.comp.lang.c++.isocpp.proposals
Subject: Re: The Proxies - A Language Feature Decoupling
 Implementations from Requirements of Polymorphism
Date: Wed, 14 Jun 2017 10:13:47 -0700 (PDT)
Lines: 396
Approved: news@gmane.org
Message-ID: <b8fa33b0-81ab-4e87-bf9b-0372a4219e26@isocpp.org>
References: <8d79a013-fe0e-4fb6-8278-d438ae9b4c64@isocpp.org>
 <6416578b-a1b5-4124-8334-f735460038b5@isocpp.org>
 <b61a514a-3a2b-4ce5-8bd3-984d547657a0@isocpp.org>
Reply-To: std-proposals@isocpp.org
NNTP-Posting-Host: blaine.gmane.org
Mime-Version: 1.0
Content-Type: multipart/mixed; 
	boundary="----=_Part_612_1280741241.1497460427462"
X-Trace: blaine.gmane.org 1497460429 28623 195.159.176.226 (14 Jun 2017 17:13:49 GMT)
X-Complaints-To: usenet@blaine.gmane.org
NNTP-Posting-Date: Wed, 14 Jun 2017 17:13:49 +0000 (UTC)
To: ISO C++ Standard - Future Proposals <std-proposals@isocpp.org>
Original-X-From: std-proposals+bncBCEKFTV6ZUMBBTG5QXFAKGQE5R7MYLI@isocpp.org Wed Jun 14 19:13:44 2017
Return-path: <std-proposals+bncBCEKFTV6ZUMBBTG5QXFAKGQE5R7MYLI@isocpp.org>
Envelope-to: gclcip-std-proposals@m.gmane.org
Original-Received: from mail-oi0-f70.google.com ([209.85.218.70])
	by blaine.gmane.org with esmtp (Exim 4.84_2)
	(envelope-from <std-proposals+bncBCEKFTV6ZUMBBTG5QXFAKGQE5R7MYLI@isocpp.org>)
	id 1dLBrU-00077D-6a
	for gclcip-std-proposals@m.gmane.org; Wed, 14 Jun 2017 19:13:44 +0200
Original-Received: by mail-oi0-f70.google.com with SMTP id b6sf3402527oia.14
        for <gclcip-std-proposals@m.gmane.org>; Wed, 14 Jun 2017 10:13:49 -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=fbpEyzyLyUQhqJOGfsvCyiVpU3Wyixit45+gwzsI6lM=;
        b=B7WsehXntwCs7pcPjVCBZxnXcEq7ooiddN2ANUiFPAn91mQLLrxET3Wzkib9Q+m0U9
         5NF9GJ9ZRJ7b/ameN+G16XK7md1w8zYJ94VknuQd4aQxmpJ93BwimGqyVOFq0Xztut+s
         aUJNsAStWjGQeQy9l93HAqj9Ke3Bpqk6RFmRze/ncnC/61CivBV/BF45P3vGws5NWXxb
         DO+2C8oK7Mx+tL22zQGGxm8ctVxzp4lUtii0vse8Sa8+WXt6eqYiBuy6q4c4vlBiq6YN
         LsR1TFAEG+GLTPp+0F4KycmH9G/SF9ucQDn4hMYMkz/x+jWjgNVlU4E+YxPxS2zHcOfP
         0b1g==
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=fbpEyzyLyUQhqJOGfsvCyiVpU3Wyixit45+gwzsI6lM=;
        b=LFuILMpANkgNaMn9hWyDNob2Z68bnL5TLgf67HEpMStBF50tXll5RindUL9bP6O1Hy
         PX2Kzuu3G7+In+lf/GxTKBbm1kF9ojr5aDI69e44P5aS9coY3y/GI+T5aAnEAyhhJ8yI
         W6nj7yWqwF6XYfjVCMzvO77V4YEFiCM6n0APrhjBWihyIuGCLBnSIIqcHZoF8HNivRcR
         PChxZe3CQ3O04S8xz4vWVSaoZuepGQb6HxANt6B/K/YFf2ksUEVH40ed72SzzZlqdHOJ
         fVB31K/wjMCOC2BbFbBOurdJdQ2OXwb60CKHLpaRauHpFtEDlfgekM0Afaf1hJ/Ba9x4
         V8Mw==
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=fbpEyzyLyUQhqJOGfsvCyiVpU3Wyixit45+gwzsI6lM=;
        b=QznQ1exjlmR6tSzBxw3eqBWmlCNG0JPfYhWKRnz8J5qxGEBsrFRLwjU9kgK3np94uY
         ES34ofi0x7uhqXCZxxkx8QEguBGjsTQ3p6wVkwXhyu+vVpVWbgf0u6hO0HoHiPbO/xJG
         R35ZyjUS7Of2meiC0eOSEL5aHhNkiR01KpPsr9ym61u7iLI0YFdfHv3FqGb089aQ9gjR
         yAqAFMH01QmE/Ms6iJIizlqLeBK/7F2QQg6aBU4ukJ/unz1Xc2DWore3XJlNrNTH6iMM
         OSiMLTp4Qt7vib3Vvdev3Ie9QHwT3ts/dJghLuw+ElPyN44LwE/LcMhKYi+lhLbIdYBp
         s+UA==
X-Gm-Message-State: AKS2vOxRIzLqW8YBiyIcQPv7x7wWE7Mqra+BJXrKEooaL5zlhj1R4KrO
	9CwwGt6vTSeYe3Ec
X-Received: by 10.157.37.117 with SMTP id j50mr575250otd.39.1497460429088;
        Wed, 14 Jun 2017 10:13:49 -0700 (PDT)
X-BeenThere: std-proposals@isocpp.org
Original-Received: by 10.157.11.227 with SMTP id 90ls547587oth.3.gmail; Wed, 14 Jun
 2017 10:13:48 -0700 (PDT)
X-Received: by 10.157.24.54 with SMTP id b51mr32645ote.14.1497460428077;
        Wed, 14 Jun 2017 10:13:48 -0700 (PDT)
In-Reply-To: <b61a514a-3a2b-4ce5-8bd3-984d547657a0@isocpp.org>
X-Original-Sender: jmckesson@gmail.com
Precedence: list
Mailing-list: list std-proposals@isocpp.org; contact std-proposals+owners@isocpp.org
List-ID: <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:32775
Archived-At: <http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/32775>

------=_Part_612_1280741241.1497460427462
Content-Type: multipart/alternative; 
	boundary="----=_Part_613_1095511336.1497460427462"

------=_Part_613_1095511336.1497460427462
Content-Type: text/plain; charset="UTF-8"

On Wednesday, June 14, 2017 at 4:11:55 AM UTC-4, Mingxin Wang wrote:
>
> On Tuesday, June 13, 2017 at 11:20:26 PM UTC+8, Nicol Bolas wrote:
>>
>> On Tuesday, June 13, 2017 at 9:49:43 AM UTC-4, Mingxin Wang wrote:
>>>
>>> *Technical Specifications*
>>>
>>> *Wrapper requirements*
>>>
>>> A type W meets the Wrapper requirements if the following expressions are 
>>> well-formed and have the specific semantics (w denotes a value of type W).
>>>
>>> w.get()
>>> Requires: w is initialized with an object.
>>> Effects: acquires the pointer of the wrapped object if there is one.
>>> Return type: void*.
>>> Returns: a pointer of the wrapped object.
>>>
>>>
>> This seems decidedly thin on details.
>>
>> As I understand your design, the "wrapper" provides both the storage for 
>> the object/pointer as well as determining the semantics relative to that 
>> object/pointer. This is not entirely clear from your technical 
>> specifications, but everything else I'm going to say is based on this 
>> assumption.
>>
>> A "Wrapper" is only required to support semantics for addressing (at 
> minimum).
>
 
>
First, "wrapper" is a *terrible* name for this concept. It isn't "wrapping" 
>> anything; it's providing storage and specifying the semantics of the proxy 
>> (which also isn't a particularly good name). That isn't "wrapping". It's 
>> simply providing the semantics of the type. So "Semantics" seems a much 
>> more descriptive name.
>>
>> I think "Semantics" is not as good as "Wrapper". As this is a relatively 
> subjective issue, maybe we need more feedback and discuss about it later.
>  
>
>> Second, the `get` interface is entirely insufficient to fully define this 
>> concept. The "wrapper" needs to be able to be initialized from any object 
>> that matches `I`, so that has to be part of its interface. Your example 
>> code shows this, but these "Wrapper requirements" don't mention it. 
>> Furthermore:
>>
>> There is no particular requirements on "what type can be used to 
> construct a wrapper". Actually, a type that meets the Wrapper requirements 
> may carry such constraints on its constructors.
>

Which means that there is a requirement that the "wrapper" is 
*constructible* from a reference to some non-zero set of types, yes?

Also, if important aspects of the "wrapper"'s interface are not 
"requirements", can we get a section that details all of the *optional* 
parts of its interface too?

Third, it's not clear who is actually responsible for the *type erasure* 
>> part of this. The type `T` provided to a "proxy" seems to be erased by the 
>> code generated data. But since the "wrapper" holds the storage for said 
>> `T`, it must also have enough information to *destroy* that object. 
>> After all, the "wrapper" may copy/move from the given `T` into new storage 
>> for that `T`. Which means that the "wrapper" implementation has to know 
>> what that `T` was when it goes to destroy it.
>>
>> A Wrapper may be responsible to destroy an object just as std::any does, 
> and a Proxy is responsible for ACCESSING the object without RTTI.
>  
>
>> This means that any "wrapper" that actually owns a `T` must *itself* 
>> perform type-erasure, for the purpose of destroying the `T`. We discussed 
>> this in your last thread. Double-type-erasure is less efficient than 
>> single-type-erasure, thus violating one of your design goals.
>>
>> Not exactly. Double-type-erasure only requires a little more memory 
> (one-pointer size) and is more compatible.
>

Since you defined "efficiency" as "performance", OK. But it is also 
misleading, since "efficiency" means more than just runtime performance. So 
while you follow the letter of your statement, the apparent *spirit* of it 
(that a user should gain nothing from hand-writing their own) is still 
violated.

Also, it's not clear what you mean by "is more compatible". With what would 
it be "compatible"?

That is why we usually use "combination instead of inheritance" in 
> architecture designing. Besides, I have ran a performance test for 
> DeepWrapper<Callable<void()>, 16u> and std::function<void()>
>

That's the wrong test. A proper test would be between "DeepWrapper" and a 
hand-crafted equivalent. `std::function` has additional stuff in it that 
has no analog in the "DeepWrapper" proxy.

Furthermore, a comprehensive test should examine the size difference 
between the two objects, as well as looking at the generated code with an 
eye to code size (type-erasure tends to induce bloat).

which have the same SOO size on my compiler (Windows 7 x64, gcc version 
> 6.3.0 x86_64-posix-seh-rev2, Built by MinGW-W64 project, Command: g++.exe 
> -march=corei7-avx -O2 -m64 -fconcepts -std=c++17), and the result turns out 
> to be positive that DeepWrapper<Callable<void()>, 16u> is more efficient 
> than the implementation of std::function<void()> most of the time because 
> there is an extra addressing operation for the virtual table for 
> std::function<void()>, as is shown below (x: the size of the object for 
> type erasure; y: the time elapsed):
>
>  
>
>> Now, you might be able to work around this problem. You could design it 
>> so that the "wrapper" explicitly advertises in its interface that it is an 
>> "owning" wrapper, such that it needs to be provided with the un-erased `T` 
>> when the "proxy" is being destroyed. But that's a much more complex 
>> interface than you have outlined here.
>>
>
> This is not always necessary, especially when W is a trivial type.
>

I'm not sure I understand your point here.

As I understand this feature, the whole point of "wrappers" is to allow 
support for non-reference semantics. Any wrappers that provide 
non-reference semantics would *have* to be non-trivial. So what does it 
matter if a more complex interface isn't needed for trivial "wrappers"? 
Many of the non-trivial cases could really use that interface, and support 
for those cases is exactly why "wrapper" exists.

Also, we're not exactly talking about a complex interface here. The only 
reason double-erasure is needed is because your interface between the proxy 
and the wrapper is based on the wrapper's special member functions. The 
destructor of the Wrapper is what calls the destructor of the "wrapped" 
object. If the Wrapper is copyable, then its copy constructor is what 
copies the "wrapped" type. Etc.

This is why the term "wrapper" is wrong. Thinking of the object as being a 
wrapper means that you think of it as potentially exposing the semantics of 
the underlying type though its native copy/move/destructor methods. And 
that requires that "wrapper" know the type *internally*, which requires 
that it erase that type.

If we redefine this on a conceptual level, then it becomes clear how we can 
solve the double-erasure problem. I'll rename the type, so it's clear when 
I'm talking about one vs. the other.

A "semantic" is an object which provides storage and semantics for a value. 
However, it doesn't know what the type of that value is; that is maintained 
by the user of the class.

As such, the compiler-generated proxy would generate functions in its 
abstract base class based on the interface the "semantic" provides. The 
`T`-specific derived classes would implement versions that call the 
"semantic"'s interface functions, specifying the `T` that they work with. 
This means those interface functions in the "semantic" must be template 
functions. Note that in most cases, the "semantic" interfaces don't need to 
be provided with the `T` object itself; just the `T` that is being utilized.

The available "semantic" interfaces would be:

* `bind`: Stores a given `T`. Required, though it can refuse to accept `T`s 
that don't fit some requirement. The proxy will forward such requirements.
* `get`: Given the type `T` to retrieve, retrieves a pointer to that type. 
This is required.
* `copy`: Copies the `T` from one "semantic" into an unbound "semantic". If 
this is deleted, then the proxy is non-copyable. If this is not present, 
then the proxy will just copy the "semantic" object directly.
* `move`: Moves the `T` from one "semantic" into another. If this is 
deleted, then the proxy is non-moveable. If this is not present, then the 
proxy will just move the "semantic" object directly.
* `unbind`: Unbinds the `T`, potentially calling its destructor. If this 
function is not present, then the proxy will assume calling the destructor 
is enough.

The proxy should forward any `noexcept` guarantees of these functions. The 
proxy will also generate the code that is needed to handle assignment (as 
`unbind` followed by `copy`/`move`). Though this does lead to the `variant` 
question of what happens if a copy/move assignment fails.

A trivial "semantic" only implements `bind` and `get` (the latter only 
being needed for the `any_cast` equivalent), allowing the 
compiler-generated copy/move constructor/assignments to work. Implementing 
`copy|move` requires also implementing `unbind`, and implementing `unbind` 
requires implementing `copy|move`.

This puts all of the type-erasure machinery in one place: the proxy. This 
means that "ValueSemantic" doesn't need any storage beyond the SSO arena.

-- 
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 email 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/b8fa33b0-81ab-4e87-bf9b-0372a4219e26%40isocpp.org.

------=_Part_613_1095511336.1497460427462
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">On Wednesday, June 14, 2017 at 4:11:55 AM UTC-4, Mingxin W=
ang wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left:=
 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">On =
Tuesday, June 13, 2017 at 11:20:26 PM UTC+8, Nicol Bolas wrote:<blockquote =
class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #=
ccc solid;padding-left:1ex"><div dir=3D"ltr">On Tuesday, June 13, 2017 at 9=
:49:43 AM UTC-4, Mingxin Wang wrote:<blockquote class=3D"gmail_quote" style=
=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"=
><div dir=3D"ltr"><div></div><div><font size=3D"6"><b>Technical Specificati=
ons</b></font></div><div><br></div><div><font size=3D"4"><b>Wrapper require=
ments</b></font></div><div><br></div><div>A type W meets the Wrapper requir=
ements if the following expressions are well-formed and have the specific s=
emantics (w denotes a value of type W).</div><div><br></div><div>w.get()</d=
iv><div><span style=3D"white-space:pre">	</span>Requires: w is initialized =
with an object.</div><div><span style=3D"white-space:pre">	</span>Effects: =
acquires the pointer of the wrapped object if there is one.</div><div><span=
 style=3D"white-space:pre">	</span>Return type: void*.</div><div><span styl=
e=3D"white-space:pre">	</span>Returns: a pointer of the wrapped object.</di=
v><div><br></div></div></blockquote><div><br>This seems decidedly thin on d=
etails.<br><br>As I understand your design, the &quot;wrapper&quot; provide=
s both the storage for the object/pointer as well as determining the semant=
ics relative to that object/pointer. This is not entirely clear from your t=
echnical specifications, but everything else I&#39;m going to say is based =
on this assumption.<br><br></div></div></blockquote><div>A &quot;Wrapper&qu=
ot; is only required to support semantics for addressing (at minimum).</div=
></div></blockquote><blockquote class=3D"gmail_quote" style=3D"margin: 0px =
0px 0px 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex=
;"><div>=C2=A0</div></blockquote><div></div><blockquote class=3D"gmail_quot=
e" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;paddin=
g-left: 1ex;"><div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"m=
argin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div=
 dir=3D"ltr"><div>First, &quot;wrapper&quot; is a <i>terrible</i> name for =
this concept. It isn&#39;t &quot;wrapping&quot; anything; it&#39;s providin=
g storage and specifying the semantics of the proxy (which also isn&#39;t a=
 particularly good name). That isn&#39;t &quot;wrapping&quot;. It&#39;s sim=
ply providing the semantics of the type. So &quot;Semantics&quot; seems a m=
uch more descriptive name.<br><br></div></div></blockquote><div>I think &qu=
ot;Semantics&quot; is not as good as &quot;Wrapper&quot;. As this is a rela=
tively subjective issue, maybe we need more feedback and discuss about it l=
ater.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"marg=
in:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div di=
r=3D"ltr"><div>Second, the `get` interface is entirely insufficient to full=
y define this concept. The &quot;wrapper&quot; needs to be able to be initi=
alized from any object that matches `I`, so that has to be part of its inte=
rface. Your example code shows this, but these &quot;Wrapper requirements&q=
uot; don&#39;t mention it. Furthermore:<br><br></div></div></blockquote><di=
v>There is no particular requirements on &quot;what type can be used to con=
struct a wrapper&quot;. Actually, a type that meets the Wrapper requirement=
s may carry such constraints on its constructors.</div></div></blockquote><=
div><br>Which means that there is a requirement that the &quot;wrapper&quot=
; is <i>constructible</i> from a reference to some non-zero set of types, y=
es?<br><br>Also, if important aspects of the &quot;wrapper&quot;&#39;s inte=
rface are not &quot;requirements&quot;, can we get a section that details a=
ll of the <i>optional</i> parts of its interface too?<br><br></div><blockqu=
ote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left=
: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><blockquot=
e class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px=
 #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div>Third, it&#39;s not cle=
ar who is actually responsible for the <i>type erasure</i> part of this. Th=
e type `T` provided to a &quot;proxy&quot; seems to be erased by the code g=
enerated data. But since the &quot;wrapper&quot; holds the storage for said=
 `T`, it must also have enough information to <i>destroy</i> that object. A=
fter all, the &quot;wrapper&quot; may copy/move from the given `T` into new=
 storage for that `T`. Which means that the &quot;wrapper&quot; implementat=
ion has to know what that `T` was when it goes to destroy it.<br><br></div>=
</div></blockquote><div>A Wrapper may be responsible to destroy an object j=
ust as std::any does, and a Proxy is responsible for ACCESSING the object w=
ithout RTTI.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=
=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"=
><div dir=3D"ltr"><div>This means that any &quot;wrapper&quot; that actuall=
y owns a `T` must <i>itself</i> perform type-erasure, for the purpose of de=
stroying the `T`. We discussed this in your last thread. Double-type-erasur=
e is less efficient than single-type-erasure, thus violating one of your de=
sign goals.<br><br></div></div></blockquote><div>Not exactly. Double-type-e=
rasure only requires a little more memory (one-pointer size) and is more co=
mpatible.</div></div></blockquote><div><br>Since you defined &quot;efficien=
cy&quot; as &quot;performance&quot;, OK. But it is also misleading, since &=
quot;efficiency&quot; means more than just runtime performance. So while yo=
u follow the letter of your statement, the apparent <i>spirit</i> of it (th=
at a user should gain nothing from hand-writing their own) is still violate=
d.<br><br>Also, it&#39;s not clear what you mean by &quot;is more compatibl=
e&quot;. With what would it be &quot;compatible&quot;?<br><br></div><blockq=
uote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-lef=
t: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>That is why we =
usually use &quot;combination instead of inheritance&quot; in architecture =
designing. Besides, I have ran a performance test for DeepWrapper&lt;Callab=
le&lt;void()&gt;, 16u&gt; and std::function&lt;void()&gt;</div></div></bloc=
kquote><div><br>That&#39;s the wrong test. A proper test would be between &=
quot;DeepWrapper&quot; and a hand-crafted equivalent. `std::function` has a=
dditional stuff in it that has no analog in the &quot;DeepWrapper&quot; pro=
xy.<br><br>Furthermore, a comprehensive test should examine the size differ=
ence between the two objects, as well as looking at the generated code with=
 an eye to code size (type-erasure tends to induce bloat).<br><br></div><bl=
ockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border=
-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>which have =
the same SOO size on my compiler (Windows 7 x64, gcc version 6.3.0 x86_64-p=
osix-seh-rev2, Built by MinGW-W64 project, Command: g++.exe -march=3Dcorei7=
-avx -O2 -m64 -fconcepts -std=3Dc++17), and the result turns out to be posi=
tive that DeepWrapper&lt;Callable&lt;void()&gt;, 16u&gt; is more efficient =
than the implementation of std::function&lt;void()&gt; most of the time bec=
ause there is an extra addressing operation for the virtual table for std::=
function&lt;void()&gt;, as is shown below (x: the size of the object for ty=
pe erasure; y: the time elapsed):</div><br><div>=C2=A0<br></div><blockquote=
 class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px =
#ccc solid;padding-left:1ex"><div dir=3D"ltr"><div>Now, you might be able t=
o work around this problem. You could design it so that the &quot;wrapper&q=
uot; explicitly advertises in its interface that it is an &quot;owning&quot=
; wrapper, such that it needs to be provided with the un-erased `T` when th=
e &quot;proxy&quot; is being destroyed. But that&#39;s a much more complex =
interface than you have outlined here.</div></div></blockquote><div><br></d=
iv><div>This is not always necessary, especially when W is a trivial type.<=
/div></div></blockquote><div><br>I&#39;m not sure I understand your point h=
ere.<br><br>As I understand this feature, the whole point of &quot;wrappers=
&quot; is to allow support for non-reference semantics. Any wrappers that p=
rovide non-reference semantics would <i>have</i> to be non-trivial. So what=
 does it matter if a more complex interface isn&#39;t needed for trivial &q=
uot;wrappers&quot;? Many of the non-trivial cases could really use that int=
erface, and support for those cases is exactly why &quot;wrapper&quot; exis=
ts.<br><br>Also, we&#39;re not exactly talking about a complex interface he=
re. The only reason double-erasure is needed is because your interface betw=
een the proxy and the wrapper is based on the wrapper&#39;s special member =
functions. The destructor of the Wrapper is what calls the destructor of th=
e &quot;wrapped&quot; object. If the Wrapper is copyable, then its copy con=
structor is what copies the &quot;wrapped&quot; type. Etc.<br><br>This is w=
hy the term &quot;wrapper&quot; is wrong. Thinking of the object as being a=
 wrapper means that you think of it as potentially exposing the semantics o=
f the underlying type though its native copy/move/destructor methods. And t=
hat requires that &quot;wrapper&quot; know the type <i>internally</i>, whic=
h requires that it erase that type.<br><br>If we redefine this on a concept=
ual level, then it becomes clear how we can solve the double-erasure proble=
m. I&#39;ll rename the type, so it&#39;s clear when I&#39;m talking about o=
ne vs. the other.<br><br>A &quot;semantic&quot; is an object which provides=
 storage and semantics for a value. However, it doesn&#39;t know what the t=
ype of that value is; that is maintained by the user of the class.<br><br>A=
s such, the compiler-generated proxy would generate functions in its abstra=
ct base class based on the interface the &quot;semantic&quot; provides. The=
 `T`-specific derived classes would implement versions that call the &quot;=
semantic&quot;&#39;s interface functions, specifying the `T` that they work=
 with. This means those interface functions in the &quot;semantic&quot; mus=
t be template functions. Note that in most cases, the &quot;semantic&quot; =
interfaces don&#39;t need to be provided with the `T` object itself; just t=
he `T` that is being utilized.<br><br>The available &quot;semantic&quot; in=
terfaces would be:<br><br>* `bind`: Stores a given `T`. Required, though it=
 can refuse to accept `T`s that don&#39;t fit some requirement. The proxy w=
ill forward such requirements.<br>* `get`: Given the type `T` to retrieve, =
retrieves a pointer to that type. This is required.<br>* `copy`: Copies the=
 `T` from one &quot;semantic&quot; into an unbound &quot;semantic&quot;. If=
 this is deleted, then the proxy is non-copyable. If this is not present, t=
hen the proxy will just copy the &quot;semantic&quot; object directly.<br>*=
 `move`: Moves the `T` from one &quot;semantic&quot; into another. If this =
is deleted, then the proxy is non-moveable. If this is not present, then th=
e proxy will just move the &quot;semantic&quot; object directly.<br>* `unbi=
nd`: Unbinds the `T`, potentially calling its destructor. If this function =
is not present, then the proxy will assume calling the destructor is enough=
..<br><br>The proxy should forward any `noexcept` guarantees of these functi=
ons. The proxy will also generate the code that is needed to handle assignm=
ent (as `unbind` followed by `copy`/`move`). Though this does lead to the `=
variant` question of what happens if a copy/move assignment fails.<br><br>A=
 trivial &quot;semantic&quot; only implements `bind` and `get` (the latter =
only being needed for the `any_cast` equivalent), allowing the compiler-gen=
erated copy/move constructor/assignments to work. Implementing `copy|move` =
requires also implementing `unbind`, and implementing `unbind` requires imp=
lementing `copy|move`.<br><br>This puts all of the type-erasure machinery i=
n one place: the proxy. This means that &quot;ValueSemantic&quot; doesn&#39=
;t need any storage beyond the SSO arena.<br></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/b8fa33b0-81ab-4e87-bf9b-0372a4219e26%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/b8fa33b0-81ab-4e87-bf9b-0372a4219e26=
%40isocpp.org</a>.<br />

------=_Part_613_1095511336.1497460427462--

------=_Part_612_1280741241.1497460427462--

.
