220 32790 <0ef326e6-8a4b-4cab-8b06-a920e520ce71@isocpp.org> article
Path: news.gmane.org!.POSTED!not-for-mail
From: Mingxin Wang <wmx16835vv@163.com>
Newsgroups: gmane.comp.lang.c++.isocpp.proposals
Subject: Re: The Proxies - A Language Feature Decoupling
 Implementations from Requirements of Polymorphism
Date: Fri, 16 Jun 2017 18:07:13 -0700 (PDT)
Lines: 507
Approved: news@gmane.org
Message-ID: <0ef326e6-8a4b-4cab-8b06-a920e520ce71@isocpp.org>
References: <8d79a013-fe0e-4fb6-8278-d438ae9b4c64@isocpp.org>
 <6416578b-a1b5-4124-8334-f735460038b5@isocpp.org>
 <b61a514a-3a2b-4ce5-8bd3-984d547657a0@isocpp.org>
 <b8fa33b0-81ab-4e87-bf9b-0372a4219e26@isocpp.org>
Reply-To: std-proposals@isocpp.org
NNTP-Posting-Host: blaine.gmane.org
Mime-Version: 1.0
Content-Type: multipart/mixed; 
	boundary="----=_Part_1931_1429338444.1497661633180"
X-Trace: blaine.gmane.org 1497661636 20019 195.159.176.226 (17 Jun 2017 01:07:16 GMT)
X-Complaints-To: usenet@blaine.gmane.org
NNTP-Posting-Date: Sat, 17 Jun 2017 01:07:16 +0000 (UTC)
To: ISO C++ Standard - Future Proposals <std-proposals@isocpp.org>
Original-X-From: std-proposals+bncBDNMBNHJWIGBBQUBSLFAKGQECQK5W3Y@isocpp.org Sat Jun 17 03:07:11 2017
Return-path: <std-proposals+bncBDNMBNHJWIGBBQUBSLFAKGQECQK5W3Y@isocpp.org>
Envelope-to: gclcip-std-proposals@m.gmane.org
Original-Received: from mail-pf0-f197.google.com ([209.85.192.197])
	by blaine.gmane.org with esmtp (Exim 4.84_2)
	(envelope-from <std-proposals+bncBDNMBNHJWIGBBQUBSLFAKGQECQK5W3Y@isocpp.org>)
	id 1dM2Ck-0004tW-It
	for gclcip-std-proposals@m.gmane.org; Sat, 17 Jun 2017 03:07:11 +0200
Original-Received: by mail-pf0-f197.google.com with SMTP id w12sf51271419pfk.1
        for <gclcip-std-proposals@m.gmane.org>; Fri, 16 Jun 2017 18:07:16 -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=9j5zjuGPUK1hzSABxYEwImyiORibsvzEop2RWDIarJo=;
        b=L7cAIonpJ+oGrEABJCHvYKuVWB5/Ngw+iIF9cgk8PitHr+VCuMyq26/k1eigyJcGuT
         oOQ145YgbRCfvcDj+KQIhsVWyiFGjPNjfuGJ+M3U9ug5yKNo12JN310gLIMcIc8bkSZ2
         bsOw7/r6djH/kqVPwQyGqEK1EUx0jPZ/L7mTen+M1o/DLf9ug/hNEiCJWNSzeR/S5zaS
         0uI9fdJdjReFn2xDb67aWu4hszSnIZHQe3n4vzCF+0nIWHmJHqVWaRbxln+eDbBGzFLk
         jni5T/FOPACcPm1gf+K8Iu5HO4tBPmfVoz+dL0USK784ePRQM2NtPyh9VJD3zlKwHc8g
         HmVA==
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=9j5zjuGPUK1hzSABxYEwImyiORibsvzEop2RWDIarJo=;
        b=kEFCb3peRdPszPpqr757jHWhiokTSZllbGVSP5caPVcwG51Sp4UM2IPy86+ciYUFkJ
         iHGIur64bC+qUrIf/7Xel2kriVfHa+05Jv3VE4LwoPknN6FzXuYTMnfck8GH+Dp8G6v0
         eTnZLZ/F75pfQWhTTqLPh5yW+go//OzZWBVNVw5IAcaGD8GbrqHsObrHdpkNgMSCGpHs
         i5Hp4YGvjrCRGoBLbzS424uyPCw7dPaCA3GDltwEctmuiByN0rltaxh8u28l3f66XKGE
         dP8wvzZ6MpCra8CGWxym/KjNE1i0vD639fD/BvzhawoIpDDGKMkZnqaViXTH1Nk8G6nI
         12RQ==
X-Gm-Message-State: AKS2vOxPb/MhdlUkYezUmImY8OYVAAD1/vL/CeWTzNTyd/+XlCEfk2u6
	XLKZwpgybqRaHXhP
X-Received: by 10.99.9.195 with SMTP id 186mr7223135pgj.138.1497661635423;
        Fri, 16 Jun 2017 18:07:15 -0700 (PDT)
X-BeenThere: std-proposals@isocpp.org
Original-Received: by 10.107.47.148 with SMTP id v20ls730763iov.30.gmail; Fri, 16 Jun
 2017 18:07:13 -0700 (PDT)
X-Received: by 10.36.26.4 with SMTP id 4mr172063iti.10.1497661633892;
        Fri, 16 Jun 2017 18:07:13 -0700 (PDT)
In-Reply-To: <b8fa33b0-81ab-4e87-bf9b-0372a4219e26@isocpp.org>
X-Original-Sender: wmx16835vv@163.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:32790
Archived-At: <http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/32790>

------=_Part_1931_1429338444.1497661633180
Content-Type: multipart/alternative; 
	boundary="----=_Part_1932_1046060936.1497661633181"

------=_Part_1932_1046060936.1497661633181
Content-Type: text/plain; charset="UTF-8"

On Thursday, June 15, 2017 at 1:13:47 AM UTC+8, Nicol Bolas wrote:
>
> 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?
>

Actually, a type that meets the Wrapper requirements not only can be 
constructible from any type (e.g. DeferredWrapper), but also can even not 
to be constructible from any type at all (so that proxy<I, W> is not 
constructible from any type; this case is meaningless but legal).There 
shall be constraints on constructor of a proxy, e.g. requires(T&&) { { 
W(std::forward<T>(t)) }; }. So there are no particular requirements for the 
constructor of a wrapper, e.g. T shall be CopyConstructible for 
DeepWrapper, and DeepWrapper(std::packaged_task<void()>{...}) is ill-formed 
(because std::packaged_task is not CopyConstructible). 

>
> 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"?
>

The requirements for polymorphism and addressing are fully decoupled from 
each other, so that users are free to use the proxy with any combination of 
any pure virtual class and wrappers.

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.
>
> I am sorry that "DeepWrapper" should be "DeepProxy" (which is defined in 
src.zip/proxy.hpp, DeepProxy<I, N> == proxy<I, DeepWrapper<N>>). The 
performance test is actually between DeepProxy<Callable<void()>, 16u> and 
std::function<void()>.
 

> 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).
>

As I mentioned before, the size difference between the two objects is 
"one-pointer size", which is, on my platform, 8 bytes. Note that 
DeepWrapper is not the only implementation for the Wrapper requirements, 
there are two other implementations included in src.zip (DeferredWrapper 
and SharedWrapper). The reason why I only tested 
DeepProxy<Callable<void()>, 16u> and std::function<void()> is that there 
are little utilities we have in the standard that have similar functions as 
the proxy does.

>
> 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.
>

A wrapper may support reference semantics (e.g. DeferredWrapper). The 
implementation for the copy/move semantics for a wrapper may not 
necessarily to be polymorphic (except for "DeepWrapper", the other two 
implementations included in src.zip are not polymorphic at all). After all, 
where there is polymorphism, there is overhead.

>
> 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.
>

Take the 3 implementations of the wrapper included in src.zip as an 
example, the necessity of the copy/move/destructor to be polymorphic is as 
shown below:

<https://lh3.googleusercontent.com/-dGzxpu9jnHo/WUR-OEvMpRI/AAAAAAAAAFo/KMKy9Z6QdscLeT-4IMEbbrZeXZU1zGJ7gCLcBGAs/s1600/%25E5%259B%25BE%25E7%2589%25871.png>



> 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/0ef326e6-8a4b-4cab-8b06-a920e520ce71%40isocpp.org.

------=_Part_1932_1046060936.1497661633181
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">On Thursday, June 15, 2017 at 1:13:47 AM UTC+8, Nicol Bola=
s 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 We=
dnesday, June 14, 2017 at 4:11:55 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">On Tuesday, June 13, 2017 at 1=
1: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 Wa=
ng 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></di=
v><div><font size=3D"6"><b>Technical Specifications</b></font></div><div><b=
r></div><div><font size=3D"4"><b>Wrapper requirements</b></font></div><div>=
<br></div><div>A type W meets the Wrapper requirements if the following exp=
ressions are well-formed and have the specific semantics (w denotes a value=
 of type W).</div><div><br></div><div>w.get()</div><div><span style=3D"whit=
e-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 style=3D"white-space:pre">	</sp=
an>Returns: a pointer of the wrapped object.</div><div><br></div></div></bl=
ockquote><div><br>This seems decidedly thin on details.<br><br>As I underst=
and your design, the &quot;wrapper&quot; 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, bu=
t everything else I&#39;m going to say is based on this assumption.<br><br>=
</div></div></blockquote><div>A &quot;Wrapper&quot; is only required to sup=
port semantics for addressing (at minimum).</div></div></blockquote><blockq=
uote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1p=
x solid rgb(204,204,204);padding-left:1ex"><div>=C2=A0</div></blockquote><d=
iv></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"><blockquo=
te class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1p=
x #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div>First, &quot;wrapper&q=
uot; is a <i>terrible</i> name for this concept. It isn&#39;t &quot;wrappin=
g&quot; anything; it&#39;s providing storage and specifying the semantics o=
f the proxy (which also isn&#39;t a particularly good name). That isn&#39;t=
 &quot;wrapping&quot;. It&#39;s simply providing the semantics of the type.=
 So &quot;Semantics&quot; seems a much more descriptive name.<br><br></div>=
</div></blockquote><div>I think &quot;Semantics&quot; is not as good as &qu=
ot;Wrapper&quot;. As this is a relatively subjective issue, maybe we need m=
ore feedback and discuss about it later.</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>Second, the `get` interfa=
ce is entirely insufficient to fully define this concept. The &quot;wrapper=
&quot; 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 &quot;Wrapper requirements&quot; don&#39;t mention it. Furthermore:<=
br><br></div></div></blockquote><div>There is no particular requirements on=
 &quot;what type can be used to construct a wrapper&quot;. Actually, a type=
 that meets the Wrapper requirements may carry such constraints on its cons=
tructors.</div></div></blockquote><div><br>Which means that there is a requ=
irement that the &quot;wrapper&quot; is <i>constructible</i> from a referen=
ce to some non-zero set of types, yes?<br><br>Also, if important aspects of=
 the &quot;wrapper&quot;&#39;s interface are not &quot;requirements&quot;, =
can we get a section that details all of the <i>optional</i> parts of its i=
nterface too?<br></div></div></blockquote><div><br></div><div>Actually, a t=
ype that meets the Wrapper requirements not only can be constructible from =
any type (e.g. DeferredWrapper), but also can even not to be constructible =
from any type at all (so that proxy&lt;I, W&gt; is not constructible from a=
ny type; this case is meaningless but legal).There shall be constraints on =
constructor of a proxy, e.g. requires(T&amp;&amp;) { { W(std::forward&lt;T&=
gt;(t)) }; }. So there are no particular requirements for the constructor o=
f a wrapper, e.g. T shall be CopyConstructible for DeepWrapper, and DeepWra=
pper(std::packaged_task&lt;void()&gt;{...}) is ill-formed (because std::pac=
kaged_task is not CopyConstructible).=C2=A0</div><blockquote class=3D"gmail=
_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;p=
adding-left: 1ex;"><div dir=3D"ltr"><div><br></div><blockquote class=3D"gma=
il_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;pa=
dding-left:1ex"><div dir=3D"ltr"><div></div><blockquote class=3D"gmail_quot=
e" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-l=
eft:1ex"><div dir=3D"ltr"><div>Third, it&#39;s not clear who is actually re=
sponsible for the <i>type erasure</i> part of this. The type `T` provided t=
o a &quot;proxy&quot; seems to be erased by the code generated data. But si=
nce the &quot;wrapper&quot; holds the storage for said `T`, it must also ha=
ve enough information to <i>destroy</i> that object. After all, the &quot;w=
rapper&quot; may copy/move from the given `T` into new storage for that `T`=
.. Which means that the &quot;wrapper&quot; implementation has to know what =
that `T` was when it goes to destroy it.<br><br></div></div></blockquote><d=
iv>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.</div><di=
v>=C2=A0</div><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=
>This means that any &quot;wrapper&quot; that actually owns a `T` must <i>i=
tself</i> perform type-erasure, for the purpose of destroying the `T`. We d=
iscussed this in your last thread. Double-type-erasure is less efficient th=
an single-type-erasure, thus violating one of your design goals.<br><br></d=
iv></div></blockquote><div>Not exactly. Double-type-erasure only requires a=
 little more memory (one-pointer size) and is more compatible.</div></div><=
/blockquote><div><br>Since you defined &quot;efficiency&quot; as &quot;perf=
ormance&quot;, OK. But it is also misleading, since &quot;efficiency&quot; =
means more than just runtime performance. So while you follow the letter of=
 your statement, the apparent <i>spirit</i> of it (that a user should gain =
nothing from hand-writing their own) is still violated.<br><br>Also, it&#39=
;s not clear what you mean by &quot;is more compatible&quot;. With what wou=
ld it be &quot;compatible&quot;?<br></div></div></blockquote><div><br></div=
><div>The requirements for polymorphism and addressing are fully decoupled =
from each other, so that users are free to use the proxy with any combinati=
on of any pure virtual class and wrappers.</div><div><br></div><blockquote =
class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1p=
x #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><blockquote class=3D"gmai=
l_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;pad=
ding-left:1ex"><div dir=3D"ltr"><div>That is why we usually use &quot;combi=
nation instead of inheritance&quot; in architecture designing. Besides, I h=
ave ran a performance test for DeepWrapper&lt;Callable&lt;void()&gt;, 16u&g=
t; and std::function&lt;void()&gt;</div></div></blockquote><div><br>That&#3=
9;s the wrong test. A proper test would be between &quot;DeepWrapper&quot; =
and a hand-crafted equivalent. `std::function` has additional stuff in it t=
hat has no analog in the &quot;DeepWrapper&quot; proxy.<br><br></div></div>=
</blockquote><div>I am sorry that &quot;DeepWrapper&quot; should be &quot;D=
eepProxy&quot; (which is defined in src.zip/proxy.hpp, DeepProxy&lt;I, N&gt=
; =3D=3D proxy&lt;I, DeepWrapper&lt;N&gt;&gt;). The performance test is act=
ually between DeepProxy&lt;Callable&lt;void()&gt;, 16u&gt; and std::functio=
n&lt;void()&gt;.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" st=
yle=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-lef=
t: 1ex;"><div dir=3D"ltr"><div>Furthermore, a comprehensive test should exa=
mine 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 bloa=
t).<br></div></div></blockquote><div><br></div><div>As I mentioned before, =
the size difference between the two objects is &quot;one-pointer size&quot;=
, which is, on my platform, 8 bytes. Note that DeepWrapper is not the only =
implementation for the Wrapper requirements, there are two other implementa=
tions included in src.zip (DeferredWrapper and SharedWrapper). The reason w=
hy I only tested DeepProxy&lt;Callable&lt;void()&gt;, 16u&gt; and std::func=
tion&lt;void()&gt; is that there are little utilities we have in the standa=
rd that have similar functions as the proxy does.</div><blockquote class=3D=
"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc s=
olid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></div><blockquote class=
=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc s=
olid;padding-left:1ex"><div dir=3D"ltr"><div>which have the same SOO size o=
n my compiler (Windows 7 x64, gcc version 6.3.0 x86_64-posix-seh-rev2, Buil=
t by MinGW-W64 project, Command: g++.exe -march=3Dcorei7-avx -O2 -m64 -fcon=
cepts -std=3Dc++17), and the result turns out to be positive that DeepWrapp=
er&lt;Callable&lt;void()&gt;, 16u&gt; is more efficient than the implementa=
tion of std::function&lt;void()&gt; most of the time because there is an ex=
tra addressing operation for the virtual table for std::function&lt;void()&=
gt;, as is shown below (x: the size of the object for type erasure; y: the =
time elapsed):</div><br><div>=C2=A0<br></div><blockquote class=3D"gmail_quo=
te" 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 to work around this =
problem. You could design it so that the &quot;wrapper&quot; explicitly adv=
ertises in its interface that it is an &quot;owning&quot; wrapper, such tha=
t it needs to be provided with the un-erased `T` when the &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></div><div>This is not=
 always necessary, especially when W is a trivial type.</div></div></blockq=
uote><div><br>I&#39;m not sure I understand your point here.<br><br>As I un=
derstand this feature, the whole point of &quot;wrappers&quot; is to allow =
support for non-reference semantics. Any wrappers that provide non-referenc=
e 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 &quot;wrappers&quot;?=
 Many of the non-trivial cases could really use that interface, and support=
 for those cases is exactly why &quot;wrapper&quot; exists.<br></div></div>=
</blockquote><div><br></div><div>A wrapper may support reference semantics =
(e.g. DeferredWrapper). The implementation for the copy/move semantics for =
a wrapper may not necessarily to be polymorphic (except for &quot;DeepWrapp=
er&quot;, the other two implementations included in src.zip are not polymor=
phic at all). After all, where there is polymorphism, there is overhead.</d=
iv><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><br>A=
lso, we&#39;re not exactly talking about a complex interface here. The only=
 reason double-erasure is needed is because your interface between the prox=
y and the wrapper is based on the wrapper&#39;s special member functions. T=
he destructor of the Wrapper is what calls the destructor of the &quot;wrap=
ped&quot; object. If the Wrapper is copyable, then its copy constructor is =
what copies the &quot;wrapped&quot; type. Etc.<br><br>This is why the term =
&quot;wrapper&quot; is wrong. Thinking of the object as being a wrapper mea=
ns that you think of it as potentially exposing the semantics of the underl=
ying type though its native copy/move/destructor methods. And that requires=
 that &quot;wrapper&quot; know the type <i>internally</i>, which requires t=
hat it erase that type.<br></div></div></blockquote><div><br></div><div>Tak=
e the 3 implementations of the wrapper included in src.zip as an example, t=
he necessity of the copy/move/destructor to be polymorphic is as shown belo=
w:</div><div><br></div><p class=3D"separator" style=3D"text-align: center; =
clear: both;"><a imageanchor=3D"1" href=3D"https://lh3.googleusercontent.co=
m/-dGzxpu9jnHo/WUR-OEvMpRI/AAAAAAAAAFo/KMKy9Z6QdscLeT-4IMEbbrZeXZU1zGJ7gCLc=
BGAs/s1600/%25E5%259B%25BE%25E7%2589%25871.png" style=3D"margin-left: 1em; =
margin-right: 1em;"><img src=3D"https://lh3.googleusercontent.com/-dGzxpu9j=
nHo/WUR-OEvMpRI/AAAAAAAAAFo/KMKy9Z6QdscLeT-4IMEbbrZeXZU1zGJ7gCLcBGAs/s320/%=
25E5%259B%25BE%25E7%2589%25871.png" border=3D"0" style=3D"" width=3D"320" h=
eight=3D"56"></a></p><div><br></div><div><br></div><blockquote class=3D"gma=
il_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid=
;padding-left: 1ex;"><div dir=3D"ltr"><div><br>If we redefine this on a con=
ceptual level, then it becomes clear how we can solve the double-erasure pr=
oblem. I&#39;ll rename the type, so it&#39;s clear when I&#39;m talking abo=
ut one vs. the other.<br><br>A &quot;semantic&quot; is an object which prov=
ides storage and semantics for a value. However, it doesn&#39;t know what t=
he type of that value is; that is maintained by the user of the class.<br><=
br>As such, the compiler-generated proxy would generate functions in its ab=
stract base class based on the interface the &quot;semantic&quot; provides.=
 The `T`-specific derived classes would implement versions that call the &q=
uot;semantic&quot;&#39;s interface functions, specifying the `T` that they =
work with. This means those interface functions in the &quot;semantic&quot;=
 must be template functions. Note that in most cases, the &quot;semantic&qu=
ot; interfaces don&#39;t need to be provided with the `T` object itself; ju=
st the `T` that is being utilized.<br><br>The available &quot;semantic&quot=
; interfaces would be:<br><br>* `bind`: Stores a given `T`. Required, thoug=
h it can refuse to accept `T`s that don&#39;t fit some requirement. The pro=
xy will forward such requirements.<br>* `get`: Given the type `T` to retrie=
ve, 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 presen=
t, then the proxy will just copy the &quot;semantic&quot; object directly.<=
br>* `move`: Moves the `T` from one &quot;semantic&quot; into another. If t=
his is deleted, then the proxy is non-moveable. If this is not present, the=
n the proxy will just move the &quot;semantic&quot; object directly.<br>* `=
unbind`: Unbinds the `T`, potentially calling its destructor. If this funct=
ion is not present, then the proxy will assume calling the destructor is en=
ough.<br><br>The proxy should forward any `noexcept` guarantees of these fu=
nctions. The proxy will also generate the code that is needed to handle ass=
ignment (as `unbind` followed by `copy`/`move`). Though this does lead to t=
he `variant` question of what happens if a copy/move assignment fails.<br><=
br>A trivial &quot;semantic&quot; only implements `bind` and `get` (the lat=
ter only being needed for the `any_cast` equivalent), allowing the compiler=
-generated copy/move constructor/assignments to work. Implementing `copy|mo=
ve` requires also implementing `unbind`, and implementing `unbind` requires=
 implementing `copy|move`.<br><br>This puts all of the type-erasure machine=
ry in one place: the proxy. This means that &quot;ValueSemantic&quot; doesn=
&#39;t need any storage beyond the SSO arena.<br></div></div></blockquote><=
/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/0ef326e6-8a4b-4cab-8b06-a920e520ce71%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/0ef326e6-8a4b-4cab-8b06-a920e520ce71=
%40isocpp.org</a>.<br />

------=_Part_1932_1046060936.1497661633181--

------=_Part_1931_1429338444.1497661633180--

.
