Topic: The Proxies - A Language Feature Decoupling
Author: Mingxin Wang <wmx16835vv@163.com>
Date: Tue, 13 Jun 2017 06:49:42 -0700 (PDT)
Raw View
------=_Part_1692_922395550.1497361782856
Content-Type: multipart/alternative;
boundary="----=_Part_1693_3519812.1497361782857"
------=_Part_1693_3519812.1497361782857
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
*Introduction*
This thread is an update version for "Adding the Keyword "proxy" in C++=20
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/kvkgsHM=
6wFQ>"=20
and "Adding the Keyword "interface" in C++=20
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/uyfpeyE=
yW4o>
".
After a short term discussion, Mr. Bengt Gustafsson and I have come up with=
=20
a better solution to decouple implementations from requirements of=20
polymorphism. Comparing to the previous two solutions, not only does this=
=20
solution can be used wherever the two solutions are suitable for, but also=
=20
has more reasonable architecture and does not require new syntax.
Andrea Proli's paper about "Dynamic Generic Programming with Virtual=20
Concepts=20
<https://github.com/andyprowl/virtual-concepts/blob/master/draft/Dynamic%20=
Generic%20Programming%20with%20Virtual%20Concepts.pdf>"=20
is a very enlightening proposal and aims to solve the same challenge as=20
this solution does, and it has already clarified the motivation of this=20
work. However, I think there is still some room to improve. "DGPVC" is used=
=20
to refer to this paper in the following sections.
*Design Goals*
- Efficiency: If this feature is properly used, the performance shall=20
not be lower than a hand-wrote implementation without this feature for a=
=20
same requirement.
- Support for different lifetime management strategies: including but=20
not limited to reference semantics, value semantics, shared semantics, C=
OW=20
(Copy On Write)=E3=80=81SOO (Small Object Optimization, aka SBO, Small B=
uffer=20
Optimization) and other known advanced GC algorithms.
- Ease of use: a type-erased type shall behave as the concrete type.
- Not violate the rule of the type system: a type-erased type shall be a=
=20
standard-layout class.
=20
*Design Decisions*
- The type requirements shall be specified by a pure-virtual class.
=20
In my earlier post, some feedback suggests that it will be elegant to=20
specify type requirements with the Concepts TS. DGPVC is a solution that=20
adopts this. However, on the one hand, it introduces new syntax, mixing the=
=20
"concepts" with the "virtual qualifier", which makes the types ambiguous.=
=20
From the code snippets included in the paper, we can tell that "virtual=20
concept" is an "auto-generated" type. Comparing to introducing new syntax,=
=20
I prefer to make it a "magic class template", which at least "looks like a=
=20
type" and much easier to understand. On the other hand, I haven't seen much=
=20
about how to implement the entire solution introduced in DGPVC, and it=20
remains hard for me to imagine how are we supposed to implement for the=20
expressions that cannot be declared virtual, e.g. friend functions that=20
take values of the concrete type as parameters.
- The lifetime management strategies shall be specified by a type having=
=20
the "Wrapper" semantics.
=20
The "Wrapper" semantics is associated with the responsibility for=20
addressing, and may have different lifetime management strategies. It seems=
=20
difficult to extend DGPVC with other lifetime management strategies as it=
=20
only support the basic "reference semantics" and "value semantics", e.g.=20
reference-counting based algorithm and more complicated GC algorithms. In=
=20
this solution, users are free to specify different types of wrapper for any=
=20
lifetime management requirements. Besides, I think it is rude to couple the=
=20
"characteristics for construction and destruction" with other expressions=
=20
required in DGPVC. When it is not required to manage the lifetime issue=20
(e.g.,with reference semantics), the constraints related to constructors=20
and destructors are redundant; conversely, when we need value semantics, it=
=20
is natural that the type being type-eraing shall be at least=20
MoveConstructible most of the time. This problem does not exist in my=20
solution as constructors and destructors are not able to declared pure=20
virtual, and a wrapper type may carry such constraints if necessary.
- The type-erased type shall be a specification of a class template,=20
which is duck-typed by the compiler.
- When calling a specific member function of a type-erased type, the=20
parameters shall be forwarded to a "conversion table".
=20
Unlike std::function<R(Args...)> that provide a standard version of "R=20
operator()(Args...)", I think it is much general to provide the routine=20
that forward any parameter to the standard conversion table, e.g.,=20
providing "template <class... _Args> R operator()(_Args&&...)" instead of=
=20
"R operator()(Args...)". This will not have effects on the function itself,=
=20
and it is friendly for the "standard" conversion table.
*Technical Specifications*
*Wrapper requirements*
A type W meets the Wrapper requirements if the following expressions are=20
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.
*Class template proxy*
Expression "proxy<I, W>" is a well-formed type if I is a pure virtual class=
=20
(without a virtual destructor) and W is a type meets the Wrapper=20
requirements defined above.
"proxy<I, W>" is MoveConstructible if W is MoveConstructible, while=20
"proxy<I, W>" is CopyConstructible if W is CopyConstructible.
Providing p is a value of "proxy<I, W>" and i is a pointer of I,=20
"p.f(args...)" shall be a valid expression if "(*i).f(args...)" is a valid=
=20
expression, where f is any valid function name (including operator=20
overloads) and "args..." is any valid combination of values of any type.
*Prototypes for Wrappers*
Class "SharedWrapper" (with shared semantics), class template "DeepWrapper"=
=20
(with value semantics and SOO feature) and class "DefferedWrapper" (with=20
reference semantics) are designed to meet the Wrapper requirements.=20
Possible implementation is included in the attachments.
*Code Generation*
Take the "Callable" interface as an example,
template <class T>
class Callable; // undefined
/* Interface declaration with pure virtual class */
template <class R, class... Args>
class Callable<R(Args...)> {
public:
virtual R operator()(Args... args) =3D 0;
};
The code that the compiler will possibly generate for the type=20
"proxy<Callable<R(Args...)>, W>" is as shown below:
#include <system_error>
/* Auto generated specialization for proxy<Callable<R(Args...)>, W> */
template <class R, class... Args, class W> requires Wrapper<W>()
class proxy<Callable<R(Args...)>, W> {
public:
/* Construct with a value of any type */
/* More concepts may be required to check whether T is suitable for this=
=20
interface */
template <class T>
proxy(T&& data) requires
!std::is_same<std::remove_cv_t<std::remove_reference_t<T>>,=20
proxy>::value &&
requires(T t, Args&&... args) { { t(std::forward<Args>(args)...) } ->=
=20
R; }
{ init(std::forward<T>(data)); }
/* Default constructor */
proxy() { init(); }
/* Move constructor */
proxy(proxy&& lhs) { lhs.move_init(*this); }
/* Copy constructor */
proxy(const proxy& rhs) { rhs.copy_init(*this); }
/* Destructor */
~proxy() { deinit(); }
proxy& operator=3D(const proxy& rhs) {
deinit();
rhs.copy_init(*this);
return *this;
}
proxy& operator=3D(proxy&& lhs) {
deinit();
lhs.move_init(*this);
return *this;
}
template <class T>
proxy& operator=3D(T&& data) requires
!std::is_same<std::remove_cv_t<std::remove_reference_t<T>>,=20
proxy>::value {
deinit();
init(std::forward<T>(data));
return *this;
}
/* Auto generated member function */
/* (Args...) !=3D (_Args...), args... shall be forwarded to the virtual=
=20
function */
template <class... _Args>
R operator()(_Args&&... args) {
// Call the target function with polymorphism
return=20
(*reinterpret_cast<Abstraction*>(data_.get()))(std::forward<_Args>(args)...=
);
}
private:
/* Base class, extending the original interface */
class Abstraction : public Callable<R(Args...)> {
public:
Abstraction() =3D default;
/* Initialize the wrapper */
template <class T>
Abstraction(T&& data) : wrapper_(std::forward<T>(data)) {}
/* Non-virtual copy construct */
void copy_init(void* mem) const {
/* Copy the pointer of the vtable */
memcpy(mem, this, sizeof(Callable<R(Args...)>));
/* Initialize the wrapper with lvalue */
new (&reinterpret_cast<Abstraction*>(mem)->wrapper_) W(wrapper_);
}
void move_init(void* mem) {
memcpy(mem, this, sizeof(Callable<R(Args...)>));
new (&reinterpret_cast<Abstraction*>(mem)->wrapper_)=20
W(std::move(wrapper_));
}
W wrapper_; // A type-erased wrapper
};
/* A placeholder for the uninitialized state */
class Uninitialized : public Abstraction {
public:
/* Only for demonstration */
R operator()(Args...) override {
throw std::runtime_error("Using uninitialized proxy");
}
};
/* Type-specific implementation */
template <class T>
class Implementation : public Abstraction {
public:
template <class U>
Implementation(U&& data) : Abstraction(std::forward<U>(data)) {}
R operator()(Args... args) override {
/* Restore the type and call the target function */
return=20
(*reinterpret_cast<T*>(this->wrapper_.get()))(std::forward<Args>(args)...);
}
};
void init() {
new (reinterpret_cast<Uninitialized*>(data_.get())) Uninitialized();
}
/* Initialize with a concrete type and value */
template <class T>
void init(T&& data) {
new=20
(reinterpret_cast<Implementation<std::remove_reference_t<T>>*>(data_.get())=
)
Implementation<std::remove_reference_t<T>>(std::forward<T>(data));
}
/* Copy semantics */
void copy_init(proxy& rhs) const {
// Forward this operation
reinterpret_cast<const=20
Abstraction*>(data_.get())->copy_init(rhs.data_.get());
}
/* Move semantics */
void move_init(proxy& rhs) {
// Forward this operation
reinterpret_cast<Abstraction*>(data_.get())->move_init(rhs.data_.get())=
;
}
/* Destroy semantics */
void deinit() {
// Forward this operation
reinterpret_cast<Abstraction*>(data_.get())->~Abstraction();
}
/* sizeof(Uninitialized) =3D=3D sizeof(ptrdiff_t) + sizeof(W) */
MemoryBlock<sizeof(Uninitialized)> data_;
};
*Examples*
The following code is well-formed with the class template "Callable"=20
defined above:
#include <cmath>
#include "proxy.hpp"
int main() {
DeepProxy<Callable<void()>> a([] { puts("Lambda Expression 1"); });
a();
SharedProxy<Callable<int(int)>> b(&std::abs<int>);
printf("%d\n", b(-2));
auto lambda_2 =3D [] { puts("Lambda Expression 2"); };
DefferedProxy<Callable<void()>> c(lambda_2);
c();
c =3D a;
c();
return 0;
}
I am looking forward to your comments and suggestions!
Thank you!
Mingxin Wang
--=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/8d79a013-fe0e-4fb6-8278-d438ae9b4c64%40isocpp.or=
g.
------=_Part_1693_3519812.1497361782857
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div><font size=3D"6"><b>Introduction</b></font></div><div=
><br></div><div>This thread is an update version for "<a href=3D"https=
://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/kvkgsHM6wFQ">=
Adding the Keyword "proxy" in C++</a>" and "<a href=3D"=
https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/uyfpeyEy=
W4o">Adding the Keyword "interface" in C++</a>".</div><div><=
br></div><div>After a short term discussion, Mr. Bengt Gustafsson and I hav=
e come up with a better solution to decouple implementations from requireme=
nts of polymorphism. Comparing to the previous two solutions, not only does=
this solution can be used wherever the two solutions are suitable for, but=
also has more reasonable architecture and does not require new syntax.</di=
v><div><br></div><div>Andrea Proli's paper about "<a href=3D"https=
://github.com/andyprowl/virtual-concepts/blob/master/draft/Dynamic%20Generi=
c%20Programming%20with%20Virtual%20Concepts.pdf">Dynamic Generic Programmin=
g with Virtual Concepts</a>" is a very enlightening proposal and aims =
to solve the same challenge as this solution does, and it has already clari=
fied the motivation of this work. However, I think there is still some room=
to improve. "DGPVC" is used to refer to this paper in the follow=
ing sections.</div><div><br></div><div><font size=3D"6"><b>Design Goals</b>=
</font></div><div><br></div><div><ul><li>Efficiency: If this feature is pro=
perly used, the performance shall not be lower than a hand-wrote implementa=
tion without this feature for a same requirement.<br></li><li>Support for d=
ifferent lifetime management strategies: including but not limited to refer=
ence semantics, value semantics, shared semantics, COW (Copy On Write)=E3=
=80=81SOO (Small Object Optimization, aka SBO, Small Buffer Optimization) a=
nd other known advanced GC algorithms.<br></li><li>Ease of use: a type-eras=
ed type shall behave as the concrete type.<br></li><li>Not violate the rule=
of the type system: a type-erased type shall be a standard-layout class.<b=
r></li></ul></div><div><br></div><div><font size=3D"6"><b>Design Decisions<=
/b></font></div><div><br></div><div><ul><li>The type requirements shall be =
specified by a pure-virtual class.<br></li></ul></div><div>In my earlier po=
st, some feedback suggests that it will be elegant to specify type requirem=
ents with the Concepts TS. DGPVC is a solution that adopts this. However, o=
n the one hand, it introduces new syntax, mixing the "concepts" w=
ith the "virtual qualifier", which makes the types ambiguous. Fro=
m the code snippets included in the paper, we can tell that "virtual c=
oncept" is an "auto-generated" type. Comparing to introducin=
g new syntax, I prefer to make it a "magic class template", which=
at least "looks like a type" and much easier to understand. On t=
he other hand, I haven't seen much about how to implement the entire so=
lution introduced in DGPVC, and it remains hard for me to imagine how are w=
e supposed to implement for the expressions that cannot be declared virtual=
, e.g. friend functions that take values of the concrete type as parameters=
..</div><div><ul><li>The lifetime management strategies shall be specified b=
y a type having the "Wrapper" semantics.<br></li></ul></div><div>=
The "Wrapper" semantics is associated with the responsibility for=
addressing, and may have different lifetime management strategies. It seem=
s difficult to extend DGPVC with other lifetime management strategies as it=
only support the basic "reference semantics" and "value sem=
antics", e.g. reference-counting based algorithm and more complicated =
GC algorithms. In this solution, users are free to specify different types =
of wrapper for any lifetime management requirements. Besides, I think it is=
rude to couple the "characteristics for construction and destruction&=
quot; with other expressions required in DGPVC. When it is not required to =
manage the lifetime issue (e.g.,with reference semantics), the constraints =
related to constructors and destructors are redundant; conversely, when we =
need value semantics, it is natural that the type being type-eraing shall b=
e at least MoveConstructible most of the time. This problem does not exist =
in my solution as constructors and destructors are not able to declared pur=
e virtual, and a wrapper type may carry such constraints if necessary.</div=
><div><ul><li>The type-erased type shall be a specification of a class temp=
late, which is duck-typed by the compiler.<br></li><li>When calling a speci=
fic member function of a type-erased type, the parameters shall be forwarde=
d to a "conversion table".<br></li></ul></div><div>Unlike std::fu=
nction<R(Args...)> that provide a standard version of "R operato=
r()(Args...)", I think it is much general to provide the routine that =
forward any parameter to the standard conversion table, e.g., providing &qu=
ot;template <class... _Args> R operator()(_Args&&...)" i=
nstead of "R operator()(Args...)". This will not have effects on =
the function itself, and it is friendly for the "standard" conver=
sion table.</div><div><br></div><div><font size=3D"6"><b>Technical Specific=
ations</b></font></div><div><br></div><div><font size=3D"4"><b>Wrapper requ=
irements</b></font></div><div><br></div><div>A type W meets the Wrapper req=
uirements if the following expressions are well-formed and have the specifi=
c semantics (w denotes a value of type W).</div><div><br></div><div>w.get()=
</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre"> </span=
>Requires: w is initialized with an object.</div><div><span class=3D"Apple-=
tab-span" style=3D"white-space:pre"> </span>Effects: acquires the pointer o=
f the wrapped object if there is one.</div><div><span class=3D"Apple-tab-sp=
an" style=3D"white-space:pre"> </span>Return type: void*.</div><div><span c=
lass=3D"Apple-tab-span" style=3D"white-space:pre"> </span>Returns: a pointe=
r of the wrapped object.</div><div><br></div><div><font size=3D"4"><b>Class=
template proxy</b></font></div><div><br></div><div>Expression "proxy&=
lt;I, W>" is a well-formed type if I is a pure virtual class (witho=
ut a virtual destructor) and W is a type meets the Wrapper requirements def=
ined above.</div><div><br></div><div>"proxy<I, W>" is MoveC=
onstructible if W is MoveConstructible, while "proxy<I, W>"=
is CopyConstructible if W is CopyConstructible.</div><div><br></div><div>P=
roviding p is a value of "proxy<I, W>" and i is a pointer o=
f I, "p.f(args...)" shall be a valid expression if "(*i).f(a=
rgs...)" is a valid expression, where f is any valid function name (in=
cluding operator overloads) and "args..." is any valid combinatio=
n of values of any type.</div><div><br></div><div><b><font size=3D"4">Proto=
types for Wrappers</font></b></div><div><br></div><div>Class "SharedWr=
apper" (with shared semantics), class template "DeepWrapper"=
(with value semantics and SOO feature) and class "DefferedWrapper&quo=
t; (with reference semantics) are designed to meet the Wrapper requirements=
.. Possible implementation is included in the attachments.</div><div><br></d=
iv><div><font size=3D"6"><b>Code Generation</b></font></div><div><br></div>=
<div>Take the "Callable" interface as an example,</div><div><br><=
/div><div><div class=3D"prettyprint" style=3D"border: 1px solid rgb(187, 18=
7, 187); word-wrap: break-word; background-color: rgb(250, 250, 250);"><cod=
e class=3D"prettyprint"><div class=3D"subprettyprint"><font color=3D"#66006=
6"><div class=3D"subprettyprint">template <class T></div><div class=
=3D"subprettyprint">class Callable; // undefined</div><div class=3D"subpret=
typrint"><br></div><div class=3D"subprettyprint">/* Interface declaration w=
ith pure virtual class */</div><div class=3D"subprettyprint">template <c=
lass R, class... Args></div><div class=3D"subprettyprint">class Callable=
<R(Args...)> {</div><div class=3D"subprettyprint">=C2=A0public:</div>=
<div class=3D"subprettyprint">=C2=A0 virtual R operator()(Args... args) =3D=
0;</div><div class=3D"subprettyprint">};</div></font></div></code></div></=
div><div><br></div><div>The code that the compiler will possibly generate f=
or the type "proxy<Callable<R(Args...)>, W>" is as sh=
own below:</div><div><br></div><div><div class=3D"prettyprint" style=3D"bor=
der: 1px solid rgb(187, 187, 187); word-wrap: break-word; background-color:=
rgb(250, 250, 250);"><code class=3D"prettyprint"><div class=3D"subprettypr=
int"><font color=3D"#660066"><div class=3D"subprettyprint">#include <sys=
tem_error></div><div class=3D"subprettyprint"><br></div><div class=3D"su=
bprettyprint">/* Auto generated specialization for proxy<Callable<R(A=
rgs...)>, W> */</div><div class=3D"subprettyprint">template <class=
R, class... Args, class W> requires Wrapper<W>()</div><div class=
=3D"subprettyprint">class proxy<Callable<R(Args...)>, W> {</div=
><div class=3D"subprettyprint">=C2=A0public:</div><div class=3D"subprettypr=
int">=C2=A0 /* Construct with a value of any type */</div><div class=3D"sub=
prettyprint">=C2=A0 /* More concepts may be required to check whether T is =
suitable for this interface */</div><div class=3D"subprettyprint">=C2=A0 te=
mplate <class T></div><div class=3D"subprettyprint">=C2=A0 proxy(T&am=
p;& data) requires</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=
=A0 !std::is_same<std::remove_cv_t<std::remove_reference_t<T>&g=
t;, proxy>::value &&</div><div class=3D"subprettyprint">=C2=A0 =
=C2=A0 =C2=A0 requires(T t, Args&&... args) { { t(std::forward<A=
rgs>(args)...) } -> R; }</div><div class=3D"subprettyprint">=C2=A0 =
=C2=A0 =C2=A0 { init(std::forward<T>(data)); }</div><div class=3D"sub=
prettyprint"><br></div><div class=3D"subprettyprint">=C2=A0 /* Default cons=
tructor */</div><div class=3D"subprettyprint">=C2=A0 proxy() { init(); }</d=
iv><div class=3D"subprettyprint"><br></div><div class=3D"subprettyprint">=
=C2=A0 /* Move constructor */</div><div class=3D"subprettyprint">=C2=A0 pro=
xy(proxy&& lhs) { lhs.move_init(*this); }</div><div class=3D"subpre=
ttyprint"><br></div><div class=3D"subprettyprint">=C2=A0 /* Copy constructo=
r */</div><div class=3D"subprettyprint">=C2=A0 proxy(const proxy& rhs) =
{ rhs.copy_init(*this); }</div><div class=3D"subprettyprint"><br></div><div=
class=3D"subprettyprint">=C2=A0 /* Destructor */</div><div class=3D"subpre=
ttyprint">=C2=A0 ~proxy() { deinit(); }</div><div class=3D"subprettyprint">=
<br></div><div class=3D"subprettyprint">=C2=A0 proxy& operator=3D(const=
proxy& rhs) {</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 deinit(=
);</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 rhs.copy_init(*this);</=
div><div class=3D"subprettyprint">=C2=A0 =C2=A0 return *this;</div><div cla=
ss=3D"subprettyprint">=C2=A0 }</div><div class=3D"subprettyprint"><br></div=
><div class=3D"subprettyprint">=C2=A0 proxy& operator=3D(proxy&&=
; lhs) {</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 deinit();</div><d=
iv class=3D"subprettyprint">=C2=A0 =C2=A0 lhs.move_init(*this);</div><div c=
lass=3D"subprettyprint">=C2=A0 =C2=A0 return *this;</div><div class=3D"subp=
rettyprint">=C2=A0 }</div><div class=3D"subprettyprint"><br></div><div clas=
s=3D"subprettyprint">=C2=A0 template <class T></div><div class=3D"sub=
prettyprint">=C2=A0 proxy& operator=3D(T&& data) requires</div>=
<div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=A0 !std::is_same<std::re=
move_cv_t<std::remove_reference_t<T>>, proxy>::value {</div>=
<div class=3D"subprettyprint">=C2=A0 =C2=A0 deinit();</div><div class=3D"su=
bprettyprint">=C2=A0 =C2=A0 init(std::forward<T>(data));</div><div cl=
ass=3D"subprettyprint">=C2=A0 =C2=A0 return *this;</div><div class=3D"subpr=
ettyprint">=C2=A0 }</div><div class=3D"subprettyprint"><br></div><div class=
=3D"subprettyprint">=C2=A0 /* Auto generated member function */</div><div c=
lass=3D"subprettyprint">=C2=A0 /* (Args...) !=3D (_Args...), args... shall =
be forwarded to the virtual function */</div><div class=3D"subprettyprint">=
=C2=A0 template <class... _Args></div><div class=3D"subprettyprint">=
=C2=A0 R operator()(_Args&&... args) {</div><div class=3D"subpretty=
print">=C2=A0 =C2=A0 // Call the target function with polymorphism</div><di=
v class=3D"subprettyprint">=C2=A0 =C2=A0 return (*reinterpret_cast<Abstr=
action*>(data_.get()))(std::forward<_Args>(args)...);</div><div cl=
ass=3D"subprettyprint">=C2=A0 }</div><div class=3D"subprettyprint"><br></di=
v><div class=3D"subprettyprint">=C2=A0private:</div><div class=3D"subpretty=
print">=C2=A0 /* Base class, extending the original interface */</div><div =
class=3D"subprettyprint">=C2=A0 class Abstraction : public Callable<R(Ar=
gs...)> {</div><div class=3D"subprettyprint">=C2=A0 =C2=A0public:</div><=
div class=3D"subprettyprint">=C2=A0 =C2=A0 Abstraction() =3D default;</div>=
<div class=3D"subprettyprint"><br></div><div class=3D"subprettyprint">=C2=
=A0 =C2=A0 /* Initialize the wrapper */</div><div class=3D"subprettyprint">=
=C2=A0 =C2=A0 template <class T></div><div class=3D"subprettyprint">=
=C2=A0 =C2=A0 Abstraction(T&& data) : wrapper_(std::forward<T>=
;(data)) {}</div><div class=3D"subprettyprint"><br></div><div class=3D"subp=
rettyprint">=C2=A0 =C2=A0 /* Non-virtual copy construct */</div><div class=
=3D"subprettyprint">=C2=A0 =C2=A0 void copy_init(void* mem) const {</div><d=
iv class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=A0 /* Copy the pointer of the=
vtable */</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=A0 memcpy(m=
em, this, sizeof(Callable<R(Args...)>));</div><div class=3D"subpretty=
print"><br></div><div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=A0 /* Init=
ialize the wrapper with lvalue */</div><div class=3D"subprettyprint">=C2=A0=
=C2=A0 =C2=A0 new (&reinterpret_cast<Abstraction*>(mem)->wrap=
per_) W(wrapper_);</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 }</div>=
<div class=3D"subprettyprint"><br></div><div class=3D"subprettyprint">=C2=
=A0 =C2=A0 void move_init(void* mem) {</div><div class=3D"subprettyprint">=
=C2=A0 =C2=A0 =C2=A0 memcpy(mem, this, sizeof(Callable<R(Args...)>));=
</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=A0 new (&reinterp=
ret_cast<Abstraction*>(mem)->wrapper_) W(std::move(wrapper_));</di=
v><div class=3D"subprettyprint">=C2=A0 =C2=A0 }</div><div class=3D"subprett=
yprint"><br></div><div class=3D"subprettyprint">=C2=A0 =C2=A0 W wrapper_; /=
/ A type-erased wrapper</div><div class=3D"subprettyprint">=C2=A0 };</div><=
div class=3D"subprettyprint"><br></div><div class=3D"subprettyprint">=C2=A0=
/* A placeholder for the uninitialized state */</div><div class=3D"subpret=
typrint">=C2=A0 class Uninitialized : public Abstraction {</div><div class=
=3D"subprettyprint">=C2=A0 =C2=A0public:</div><div class=3D"subprettyprint"=
>=C2=A0 =C2=A0 /* Only for demonstration */</div><div class=3D"subprettypri=
nt">=C2=A0 =C2=A0 R operator()(Args...) override {</div><div class=3D"subpr=
ettyprint">=C2=A0 =C2=A0 =C2=A0 throw std::runtime_error("Using uninit=
ialized proxy");</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 }</d=
iv><div class=3D"subprettyprint">=C2=A0 };</div><div class=3D"subprettyprin=
t"><br></div><div class=3D"subprettyprint">=C2=A0 /* Type-specific implemen=
tation */</div><div class=3D"subprettyprint">=C2=A0 template <class T>=
;</div><div class=3D"subprettyprint">=C2=A0 class Implementation : public A=
bstraction {</div><div class=3D"subprettyprint">=C2=A0 =C2=A0public:</div><=
div class=3D"subprettyprint">=C2=A0 =C2=A0 template <class U></div><d=
iv class=3D"subprettyprint">=C2=A0 =C2=A0 Implementation(U&& data) =
: Abstraction(std::forward<U>(data)) {}</div><div class=3D"subprettyp=
rint"><br></div><div class=3D"subprettyprint">=C2=A0 =C2=A0 R operator()(Ar=
gs... args) override {</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=
=A0 /* Restore the type and call the target function */</div><div class=3D"=
subprettyprint">=C2=A0 =C2=A0 =C2=A0 return (*reinterpret_cast<T*>(th=
is->wrapper_.get()))(std::forward<Args>(args)...);</div><div class=
=3D"subprettyprint">=C2=A0 =C2=A0 }</div><div class=3D"subprettyprint">=C2=
=A0 };</div><div class=3D"subprettyprint"><br></div><div class=3D"subpretty=
print">=C2=A0 void init() {</div><div class=3D"subprettyprint">=C2=A0 =C2=
=A0 new (reinterpret_cast<Uninitialized*>(data_.get())) Uninitialized=
();</div><div class=3D"subprettyprint">=C2=A0 }</div><div class=3D"subprett=
yprint"><br></div><div class=3D"subprettyprint">=C2=A0 /* Initialize with a=
concrete type and value */</div><div class=3D"subprettyprint">=C2=A0 templ=
ate <class T></div><div class=3D"subprettyprint">=C2=A0 void init(T&a=
mp;& data) {</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 new (rein=
terpret_cast<Implementation<std::remove_reference_t<T>>*>=
(data_.get()))</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=A0 =C2=
=A0 Implementation<std::remove_reference_t<T>>(std::forward<=
T>(data));</div><div class=3D"subprettyprint">=C2=A0 }</div><div class=
=3D"subprettyprint"><br></div><div class=3D"subprettyprint">=C2=A0 /* Copy =
semantics */</div><div class=3D"subprettyprint">=C2=A0 void copy_init(proxy=
& rhs) const {</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 // Forw=
ard this operation</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 reinter=
pret_cast<const Abstraction*>(data_.get())->copy_init(rhs.data_.ge=
t());</div><div class=3D"subprettyprint">=C2=A0 }</div><div class=3D"subpre=
ttyprint"><br></div><div class=3D"subprettyprint">=C2=A0 /* Move semantics =
*/</div><div class=3D"subprettyprint">=C2=A0 void move_init(proxy& rhs)=
{</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 // Forward this operati=
on</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 reinterpret_cast<Abs=
traction*>(data_.get())->move_init(rhs.data_.get());</div><div class=
=3D"subprettyprint">=C2=A0 }</div><div class=3D"subprettyprint"><br></div><=
div class=3D"subprettyprint">=C2=A0 /* Destroy semantics */</div><div class=
=3D"subprettyprint">=C2=A0 void deinit() {</div><div class=3D"subprettyprin=
t">=C2=A0 =C2=A0 // Forward this operation</div><div class=3D"subprettyprin=
t">=C2=A0 =C2=A0 reinterpret_cast<Abstraction*>(data_.get())->~Abs=
traction();</div><div class=3D"subprettyprint">=C2=A0 }</div><div class=3D"=
subprettyprint"><br></div><div class=3D"subprettyprint">=C2=A0 /* sizeof(Un=
initialized) =3D=3D sizeof(ptrdiff_t) + sizeof(W) */</div><div class=3D"sub=
prettyprint">=C2=A0 MemoryBlock<sizeof(Uninitialized)> data_;</div><d=
iv class=3D"subprettyprint">};</div></font></div></code></div></div><div><b=
r></div><div><font size=3D"6"><b>Examples</b></font></div><div><br></div><d=
iv>The following code is well-formed with the class template "Callable=
" defined above:</div><div><br></div><div><div class=3D"prettyprint" s=
tyle=3D"border: 1px solid rgb(187, 187, 187); word-wrap: break-word; backgr=
ound-color: rgb(250, 250, 250);"><code class=3D"prettyprint"><div class=3D"=
subprettyprint"><font color=3D"#660066"><div class=3D"subprettyprint">#incl=
ude <cmath></div><div class=3D"subprettyprint"><br></div><div class=
=3D"subprettyprint">#include "proxy.hpp"</div><div class=3D"subpr=
ettyprint"><br></div><div class=3D"subprettyprint">int main() {</div><div c=
lass=3D"subprettyprint">=C2=A0 DeepProxy<Callable<void()>> a([]=
{ puts("Lambda Expression 1"); });</div><div class=3D"subprettyp=
rint">=C2=A0 a();</div><div class=3D"subprettyprint"><br></div><div class=
=3D"subprettyprint">=C2=A0 SharedProxy<Callable<int(int)>> b(&a=
mp;std::abs<int>);</div><div class=3D"subprettyprint">=C2=A0 printf(&=
quot;%d\n", b(-2));</div><div class=3D"subprettyprint"><br></div><div =
class=3D"subprettyprint">=C2=A0 auto lambda_2 =3D [] { puts("Lambda Ex=
pression 2"); };</div><div class=3D"subprettyprint">=C2=A0 DefferedPro=
xy<Callable<void()>> c(lambda_2);</div><div class=3D"subprettyp=
rint">=C2=A0 c();</div><div class=3D"subprettyprint">=C2=A0 c =3D a;</div><=
div class=3D"subprettyprint">=C2=A0 c();</div><div class=3D"subprettyprint"=
><br></div><div class=3D"subprettyprint">=C2=A0 return 0;</div><div class=
=3D"subprettyprint">}</div></font></div></code></div><br><div>I am looking =
forward to your comments and suggestions!</div><div><br></div><div>Thank yo=
u!</div><div><br></div><div>Mingxin Wang</div></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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/8d79a013-fe0e-4fb6-8278-d438ae9b4c64%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/8d79a013-fe0e-4fb6-8278-d438ae9b4c64=
%40isocpp.org</a>.<br />
------=_Part_1693_3519812.1497361782857--
------=_Part_1692_922395550.1497361782856
Content-Type: application/zip; name=src.zip
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=src.zip
X-Attachment-Id: 99b06136-3b52-406e-b6ec-8455c1a6a852
Content-ID: <99b06136-3b52-406e-b6ec-8455c1a6a852>
UEsDBBQAAAAIAOmqzUpPk8plJQUAAIoRAAAQAAAAZGVlcF93cmFwcGVyLmhwcL1Y204jRxB9t+R/
qICEbAdMLm+2cUQw0iLtBguMNsnLqJmpwZ2MpyfdPUYsYr891Ze52WMvu0FBApueup46VdX2IY/T
CGOY31z//kcwu7ycBx9vzufzy5vg3XweXP128f5udjnrdrqdQ5LjKb5KlKdhkkcIk1zzhOunaeP0
YIUrIZ+C+0SEfw+XWXZgHp8O4BwixOwkFNkTPEqWZSjhkesl3F5fQ4xM5xJhcNrtaFxlCdPkQOlo
NFL8EwbaSAW3V39ekrcwYUrBjKx99Haeux3I8vuEhyN6B+TtQqRKyzzUQiprFaCy6wwspua0Zqa3
ODqCiGnWB4n/5FyiMhLm5zsbCleBYit0cUnKc41BuA5040BijBLTkIKeLKbT47qH6Wi0ZkmOhdln
4CnXPaseC/nIZEQ6PRtDfwwvmwH2C422h6FJuX5yBHKpjAq9DA3ugdUd6CVXbQZmNdUjSJwuvQxt
Xpu6HugZFjh7mD9vBhxhLeQNn0cg6C8j7bPd8Tu0SjPu39ac/CMkLqVgz+zRfr9tabe6bIViv0sC
6AOiVqCXCN5JQa4Vprrg5lrwaAAPqHul89NTWJASiyLioQIRWxuucyIQ939hqIErCFkS5obXkWsn
BhZIlmrSiRXqRoRLkUQoAzg7gzRPkkxL+KV8NyIpnmqUGUkHIVN6Ei6ZHEx7Xq0P34PpRxH3zu+p
7izU7+yTfi3pTPI1hVN04tz09ZpLnbPEu3eBFmfRJoNcezYdeFRqTQ6lgc9NUQPhS1PCwAsVW+oV
78MZ/DBuEa8q3S7+Mu52do8V956mUEhQos9h5ON/RW7NEeaB2XZ2N7XHTT+9u3KOjexr0Jwwd8WE
sTg5w3sQcl1IaEjJI/TjxEpZ28UkgX2wFTa2bdjIjJK3Vo2JBpEsIiY910qhTxjsOC3hWbh0G/Uh
vStyxFlCxHUdClrAPdIkJW0KeXDqO9DPVniu2qToDYqqsHZNWSSCRUB4glqxJCm6cXDayoeG9e0d
Az3fUos+TM7KRVcfBO9RlyFlglrUZGBwIHWUa4zsErUr9xhY6v4VmeYr/olpLlIzKIhvFs/IWq0y
3Or5Jjup+5UQbp8HQzui+uNup4ztwgDga1Ly1U+rJi9t24tcg7siACmK0IZnbaX4CL3NYCb20TbH
963ctoj732Bn117uwA4yJEw+4NuRYfp6LjA4IPgOvO/dHODpLhYY9N8GmrLtLsxVT+GKVhEP65tu
76CxNGouQVogjH71o6Dto3ObjRqVEgA/Dn1j87To6uPaY3ddALU0VGWJqnf/sCb3U2lGgTMCqYAV
09qQd4lEabm/tXZ6tQ6LKeQ3tfXl/fMYetu7uaw7VAOzuHK8ACYKKwGvfTKtwDV4FtLd5qWEatpe
mn3zu7UoS4n4P5fF0fv2berQrIPV+LkW936HPN3l0tzdbHtiOQ395SdkqQlD8VWWPFm8aXpIsap2
k1z+Z1psKG7NwxbemEi2eePMNfxVQ8O/GxdPt9dmK/vsZwaxYzYUl+6vHwFf05mmPLWLZ7NCJVXo
bp1gVO4tiQkyhVbU7bANmt5efytFSpcR+SDRYVP0Ki6xNUlZeAvdY7C8cR9ojNMlW9MiSQ1SEZ5g
HNNKGFpzX2bFJim27tbjBi28uAu6pENbzc+VEiEnFJSF02KY8BgJLAKTpezBfiAC403jw5OnxMZd
pHRR2a3uQDYVu4i36uCMfbA1+9WITfya3Vh5dYnyG4dpf1qDily7q+UhphGPTYm++K3Jv1BLAwQU
AAAACABtqs1KVWd7OHgBAABXAwAAFAAAAGRlZmZlcmVkX3dyYXBwZXIuaHBwnVJda8JAEHwP5D9s
EYJKwfeoATGRFvoRxGL7dFyTjR4kl/RuEynif+9dVNoGodI8hNzu7MxldnoikylmEC+fX99YGC0W
0TIK2Xo5i+Noye7imN0/zR9ewih0HdfpGayQeDVcyCSvU4QJfVbISHFBOrCd0RBmsFO8qlABbTnB
lmuQJW2F3ACVkJawE7Q1PYRcZEiiQCi45BssUBIIrWuE4ch1kpxrDSFmGSpM1yfOvetAVb/nIvHN
Fxi9eSk1qTqhUul2EICwqHJO5n5HklVgqx2q/srzIOXEB6DwoxYKtUXZ50ZT6vtCM80LnLQHhUXZ
IEsaRr8KCi2nTIwLk1UQ3HZVAt9veF7jmXrfSjKYQr8pRTocePY8hsOlGw4MzGyG1zmNL/UT++vd
qvfXVBffGbgw4kFp3tw4PP2f5k+CK+TNVh8RSbcpOeHOS7IpOe+5dRA2SMapvQFQreTR39ZQGxUl
GhME/xt9bLvOodXqoUxFBqPRddH/AlBLAwQUAAAACABZqs1Kd0KVfzcBAACcAgAAEAAAAG1lbW9y
eV9ibG9jay5ocHCNkltrAjEQhd8D/ocBQawU9t0b1Aso1SqWgraUJU1mNTSbLMmsYMX/3uz6slqw
vg1zTuZkvqSuEiMxgeVqsd7E8/F8sdrEg9li+BxPlst4+jKcvY3GoxqrsXrwKYN3WZUROpcIXTpk
GJPjiny/UKIWTKyWHjgIa0htc5t7SDG17gAetykaegSvftAmzXnZHmgrvruv0/dx/wF6PSgqaEU1
RphmmlNI8STb7eJQTKXcZ0Jz76EyAMLduYYjgyz/0kq0GVTlZhgNYUGea+pcSZW60bjhCwt5qnYu
zMX2F/4G2AwdJ+t690ZUj9xOK8MC7CchMIAgC7TDK84BYjDtrZIt2CIFBEdwSLkzIDnxuAOnoJ9z
qq5z5683oHVqHx6kYCt23J2lj+JFPjvsVCKoo5EqgSj6/x/9AlBLAwQUAAAACABmqc1KUcn2wyEB
AABZAgAACQAAAHByb3h5LmhwcHWSPU/DMBCG90j5D6d2Kai0YmFoQhZSiUiIRKSowBKF+NJaSm1j
O3xV/e84dSpAdRfbuvfuuS8Pac0I1pA9pE/PxW2WFcn9zd1jPI99z/eGRqIMT6mUVU1LEAbGrUaJ
pPiQpRAoJ2shBv8dUJwU1bo8jvU9jRvRlBohrJpSKVhEvldxVqHQ8Mp5A0sbMDqDre8BSNStZOZ6
a6lENVqA7hWALejJCrVx3cFFBO+ckvOgk3bm3LmyJWOwj2WXdv8Skn9+BTCdQmJ8cYNMl5pyBnZK
xImJoFWUrSDfN5l1CLi2qDAZ9+a+kyhw1qE0mc0U/cZCQ56mRZ68zA3j8qo9wGMz3iN0Z+zB4SEs
cqb4xdg1OlBW+FOnWSAyQutuHK7v8QNQSwMEFAAAAAgAHavNSmQzeg0SBAAACwsAABIAAABzaGFy
ZWRfd3JhcHBlci5ocHCNVt9v2zYQfjeQ/+HqAIbs5VeLYg9O4sFIAqTAuhmug25PBE2dIg6S6JGU
Uzdo//YdScmWHDmLHyzpdPfx7uPdRx3LpIgxgdn8z7/+Zl/up/O7W/Z1Pp3N7ubsfjZjn/64+f3h
9u72qHfUOyZPWeAbnWUhsjJGuLKbFTKrubRm0rRzq3IpWqbSykzazcQBnI9gCiblGmN40ny1Qg1P
0qbQ15igxkLgqVBlYWXx2AdDC1h83ECiNGQyQStzhJwX/BFzLCyMzo96IuPGwBeP+bWCfD7qwapc
ZlKM6Q5o1RtVEFoprNLGhwFYzFcZ4cNVgFhMnLUFFC0GA4i55UPQ+G8pNRrn437vjI3HY2mY4Tle
+QeNuVojE2tmW4ZtaWRfTCYn7TUm4/GaZyXWwM8gC2kjD0B1P3EdU1Tksxhewo+XSQ7rmO7XwpXe
tg1Ap8aF0eVMqNWG+fiRTaXpBmmHDyAL8XQ58zXux1e032LNekX6z5epx9hIPqzcSlXRP7WVvn6t
ksDeFio8dlZXvUJb6gK8zZv+b+1uAjqX7STl9WWJqs+I1oBNEapF6pZznV737FrJeASPaKPt4ufn
sKAgHsfUnQZU4jHCcMWglv+gsCANCJ6J0vU7TZ6kiePg6eQ0RipJDNpWhqnKYtQMrq+hKLNsZTX8
tr0bk5csLOoVeTPBjb0SRM9oElVhQ/gFjPyOKommSzfFwt77N8NG0Sst15SOn9AwgW3fqsDGHMOe
B5EwBi8XLLooiZGKTeJJalvyDH6+CNj5uPaUiR9NC4lWOZTGxUiSBU86t5IY8uLT9+MYhIsRBf0T
2E51yABos3hGG6igTwZKCU7hfR9Scqo2DyAvLV9mCB4tSCVzPDFblRHouTzqHRaocE96Joh8rKga
VzS9gcJ91IdJsLcRo4et8o39lbUV6aFWpB2hzf0EWISoVkGueyulguddi9V91RSOT+QleUbcNLrV
5wfu6AFexOBl81Ux3y24E/LW2Gh0o1Go5qz5DcdvRCOYTSFSrQr5PfTCCRjlxyvHXOkNKO1IJgSN
Gf+GcYBuFIZPe7y+djAc0vy2TtyQoIFBOgWtFE1dgJ3UdUmkn/a6eqeMuzSru8tW+qeT0JJndOyK
lJG+RO/Lk9C6oXzmy2dV7ft5fqYKu/PcaWNXnm/PsNE5zr536qgDLNVi3WiEqds/5AZPufBtsL/x
W8+7NepNdSqQ3U17h9qm7omUY4m0mQj9mNAt1mn3KzTXgNI0wOjByFxmvPomcsj9qVcJ2vXSCW4f
wuea62qYXfz68cP8IsDJBGr1hXc70R4MwuvDG2vK5eGNdaQM3SHg5bWGasqXTTXymCWujaOXGBWh
oTnCWdnkojL/aOxfW8FGDc8gI8dYxDJx9L3hk/U/UEsBAh8AFAAAAAgA6arNSk+TymUlBQAAihEA
ABAAJAAAAAAAAAAgAAAAAAAAAGRlZXBfd3JhcHBlci5ocHAKACAAAAAAAAEAGAD9qa04SOTSAf2p
rThI5NIB/amtOEjk0gFQSwECHwAUAAAACABtqs1KVWd7OHgBAABXAwAAFAAkAAAAAAAAACAAAABT
BQAAZGVmZmVyZWRfd3JhcHBlci5ocHAKACAAAAAAAAEAGABPqsCuR+TSAU+qwK5H5NIBT6rArkfk
0gFQSwECHwAUAAAACABZqs1Kd0KVfzcBAACcAgAAEAAkAAAAAAAAACAAAAD9BgAAbWVtb3J5X2Js
b2NrLmhwcAoAIAAAAAAAAQAYAFxLWplH5NIBXEtamUfk0gFcS1qZR+TSAVBLAQIfABQAAAAIAGap
zUpRyfbDIQEAAFkCAAAJACQAAAAAAAAAIAAAAGIIAABwcm94eS5ocHAKACAAAAAAAAEAGACReE2I
RuTSAZF4TYhG5NIBkXhNiEbk0gFQSwECHwAUAAAACAAdq81KZDN6DRIEAAALCwAAEgAkAAAAAAAA
ACAAAACqCQAAc2hhcmVkX3dyYXBwZXIuaHBwCgAgAAAAAAABABgAhnbqc0jk0gGGdupzSOTSAYZ2
6nNI5NIBUEsFBgAAAAAFAAUA6QEAAOwNAAAAAA==
------=_Part_1692_922395550.1497361782856--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 13 Jun 2017 08:20:26 -0700 (PDT)
Raw View
------=_Part_4049_519534447.1497367226690
Content-Type: multipart/alternative;
boundary="----=_Part_4050_251918191.1497367226690"
------=_Part_4050_251918191.1497367226690
Content-Type: text/plain; charset="UTF-8"
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.
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.
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:
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.
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.
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.
*Class template proxy*
>
> Expression "proxy<I, W>" is a well-formed type if I is a pure virtual
> class (without a virtual destructor) and W is a type meets the Wrapper
> requirements defined above.
>
> "proxy<I, W>" is MoveConstructible if W is MoveConstructible, while
> "proxy<I, W>" is CopyConstructible if W is CopyConstructible.
>
> Providing p is a value of "proxy<I, W>" and i is a pointer of I,
> "p.f(args...)" shall be a valid expression if "(*i).f(args...)" is a valid
> expression, where f is any valid function name (including operator
> overloads) and "args..." is any valid combination of values of any type.
>
As previously mentioned, the "proxy" ought to provide `any_cast`-like
functionality. That is, the ability to extract an object/reference to the
exact `T` which was provided. Since the "proxy" type's semantics are part
of its type declaration (defined by `W`), it would be easy to specialize
the interface for reference proxies vs. value proxies.
Alternatively, you could use the `std::function::target` interface for a
lighter-weight version. But the overall point is the same: a type-erased
type ought to be able to extract what it was given.
*Prototypes for Wrappers*
>
> Class "SharedWrapper" (with shared semantics), class template
> "DeepWrapper" (with value semantics and SOO feature) and class
> "DefferedWrapper" (with reference semantics) are designed to meet the
> Wrapper requirements. Possible implementation is included in the
> attachments.
>
"DeepWrapper" is the wrong name. It provides "value semantics". Calling it
"deep" suggests "deep copying", which is *not* what this provides. It
should simply be "ValueWrapper". And there should also be a
"MoveValueWrapper", for use in cases where you want to allow users to
provide move-only types.
"DefferedWrapper" is similarly misnamed (though also misspelled). It
provides "reference semantics", so it is a "ReferenceWrapper". Admittedly,
we already have a type with that name, but that's another reason not to
call them "wrappers" at all.
Also, it's not clear what "SharedWrapper" means. Does it copy/move into
memory owned by the proxy, ala `make_shared`? Can you pass it a
`shared_ptr<T>` instead of a `T`, so that you can share ownership of the
object with the proxy? If not, why not? If you're going to allow shared
ownership of the value, then it is just as reasonable to allow shared
ownership of the value with objects that *aren't* the proxy.
This is one of the problems of going to a generic mechanism for handling
semantics: the rabbit hole of possibilities is infinite.
That being said, since the "wrapper" is part of the proxy's template
declaration, it is possible to have the "wrapper" actually influence the
proxy's interface. So the "wrapper" could affect the definition of the
proxy's template constructor from `T`, as well as the template function
which extracts a pointer/reference to the proxied type. A `SharedWrapper`
could therefore allow you to pass a `shared_ptr<T>`, as well as allow you
to extract a `shared_ptr<T>`.
--
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/6416578b-a1b5-4124-8334-f735460038b5%40isocpp.org.
------=_Part_4050_251918191.1497367226690
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Tuesday, June 13, 2017 at 9:49:43 AM UTC-4, Mingxin Wan=
g 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 Specifications</b></font></div><di=
v><br></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=
expressions are well-formed and have the specific semantics (w denotes a v=
alue of type W).</div><div><br></div><div>w.get()</div><div><span style=3D"=
white-space:pre"> </span>Requires: w is initialized with an object.</div><d=
iv><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:p=
re"> </span>Return type: void*.</div><div><span style=3D"white-space:pre"> =
</span>Returns: a pointer of the wrapped object.</div><div><br></div></div>=
</blockquote><div><br>This seems decidedly thin on details.<br><br>As I und=
erstand your design, the "wrapper" provides both the storage for =
the object/pointer as well as determining the semantics relative to that ob=
ject/pointer. This is not entirely clear from your technical specifications=
, but everything else I'm going to say is based on this assumption.<br>=
<br>First, "wrapper" is a <i>terrible</i> 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 descr=
iptive name.<br><br>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 requireme=
nts" don't mention it. Furthermore:<br><br>Third, it's not cle=
ar who is actually responsible for the <i>type erasure</i> part of this. Th=
e type `T` provided to a "proxy" seems to be erased by the code g=
enerated data. But since the "wrapper" holds the storage for said=
`T`, it must also have enough information to <i>destroy</i> that object. A=
fter all, the "wrapper" may copy/move from the given `T` into new=
storage for that `T`. Which means that the "wrapper" implementat=
ion has to know what that `T` was when it goes to destroy it.<br><br>This m=
eans that any "wrapper" that actually owns a `T` must <i>itself</=
i> perform type-erasure, for the purpose of destroying the `T`. We discusse=
d this in your last thread. Double-type-erasure is less efficient than sing=
le-type-erasure, thus violating one of your design goals.<br><br>Now, you m=
ight be able to work around this problem. You could design it so that the &=
quot;wrapper" explicitly advertises in its interface that it is an &qu=
ot;owning" wrapper, such that it needs to be provided with the un-eras=
ed `T` when the "proxy" is being destroyed. But that's a much=
more complex interface than you have outlined here.<br><br></div><blockquo=
te 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"4"><b>Class template proxy</b></font></div><div><br></div><div>Expr=
ession "proxy<I, W>" is a well-formed type if I is a pure v=
irtual class (without a virtual destructor) and W is a type meets the Wrapp=
er requirements defined above.</div><div><br></div><div>"proxy<I, W=
>" is MoveConstructible if W is MoveConstructible, while "prox=
y<I, W>" is CopyConstructible if W is CopyConstructible.</div><d=
iv><br></div><div>Providing p is a value of "proxy<I, W>" a=
nd i is a pointer of I, "p.f(args...)" shall be a valid expressio=
n if "(*i).f(args...)" is a valid expression, where f is any vali=
d function name (including operator overloads) and "args..." is a=
ny valid combination of values of any type.</div></div></blockquote><div><b=
r>As previously mentioned, the "proxy" ought to provide `any_cast=
`-like functionality. That is, the ability to extract an object/reference t=
o the exact `T` which was provided. Since the "proxy" type's =
semantics are part of its type declaration (defined by `W`), it would be ea=
sy to specialize the interface for reference proxies vs. value proxies.<br>=
<br>Alternatively, you could use the `std::function::target` interface for =
a lighter-weight version. But the overall point is the same: a type-erased =
type ought to be able to extract what it was given.<br><br></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></div><div><b><fon=
t size=3D"4">Prototypes for Wrappers</font></b></div><div><br></div><div>Cl=
ass "SharedWrapper" (with shared semantics), class template "=
;DeepWrapper" (with value semantics and SOO feature) and class "D=
efferedWrapper" (with reference semantics) are designed to meet the Wr=
apper requirements. Possible implementation is included in the attachments.=
</div></div></blockquote><div><br>"DeepWrapper" is the wrong name=
.. It provides "value semantics". Calling it "deep" sugg=
ests "deep copying", which is <i>not</i> what this provides. It s=
hould simply be "ValueWrapper". And there should also be a "=
MoveValueWrapper", for use in cases where you want to allow users to p=
rovide move-only types.<br><br>"DefferedWrapper" is similarly mis=
named (though also misspelled). It provides "reference semantics"=
, so it is a "ReferenceWrapper". Admittedly, we already have a ty=
pe with that name, but that's another reason not to call them "wra=
ppers" at all.<br><br>Also, it's not clear what "SharedWrappe=
r" means. Does it copy/move into memory owned by the proxy, ala `make_=
shared`? Can you pass it a `shared_ptr<T>` instead of a `T`, so that =
you can share ownership of the object with the proxy? If not, why not? If y=
ou're going to allow shared ownership of the value, then it is just as =
reasonable to allow shared ownership of the value with objects that <i>aren=
't</i> the proxy.<br><br> This is one of the problems of going to a gen=
eric mechanism for=20
handling semantics: the rabbit hole of possibilities is infinite.<br><br>Th=
at being said, since the "wrapper" is part of the proxy's tem=
plate declaration, it is possible to have the "wrapper" actually =
influence the proxy's interface. So the "wrapper" could affec=
t the definition of the proxy's template constructor from `T`, as well =
as the template function which extracts a pointer/reference to the proxied =
type. A `SharedWrapper` could therefore allow you to pass a `shared_ptr<=
T>`, as well as allow you to extract a `shared_ptr<T>`.<br></div><=
/div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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/6416578b-a1b5-4124-8334-f735460038b5%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/6416578b-a1b5-4124-8334-f735460038b5=
%40isocpp.org</a>.<br />
------=_Part_4050_251918191.1497367226690--
------=_Part_4049_519534447.1497367226690--
.
Author: Mingxin Wang <wmx16835vv@163.com>
Date: Wed, 14 Jun 2017 01:11:55 -0700 (PDT)
Raw View
------=_Part_4458_399239291.1497427915749
Content-Type: multipart/alternative;
boundary="----=_Part_4459_1857707340.1497427915750"
------=_Part_4459_1857707340.1497427915750
Content-Type: text/plain; charset="UTF-8"
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.
> 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. 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()> 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):
<https://lh3.googleusercontent.com/-_gjJzdIwPp4/WUDlTwYKtUI/AAAAAAAAAE8/Vl08JuotE1cfLt6bRyWB7HQtNeWmkqKBgCLcBGAs/s1600/Untitled.png>
<https://lh3.googleusercontent.com/-mDVlwZYpOgM/WUDlbYCiEBI/AAAAAAAAAFA/GNdldWXiF6MjcH54bljg9OaUSJFMQDiwACLcBGAs/s1600/Untitled.png>
<https://lh3.googleusercontent.com/-a7Z1kFHkaEA/WUDliSb7vOI/AAAAAAAAAFE/YvcQfqvOGL0AZw4i_aLW7wLZBoHbhOVFwCLcBGAs/s1600/Untitled.png>
> 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.
> *Class template proxy*
>>
>> Expression "proxy<I, W>" is a well-formed type if I is a pure virtual
>> class (without a virtual destructor) and W is a type meets the Wrapper
>> requirements defined above.
>>
>> "proxy<I, W>" is MoveConstructible if W is MoveConstructible, while
>> "proxy<I, W>" is CopyConstructible if W is CopyConstructible.
>>
>> Providing p is a value of "proxy<I, W>" and i is a pointer of I,
>> "p.f(args...)" shall be a valid expression if "(*i).f(args...)" is a valid
>> expression, where f is any valid function name (including operator
>> overloads) and "args..." is any valid combination of values of any type.
>>
>
> As previously mentioned, the "proxy" ought to provide `any_cast`-like
> functionality. That is, the ability to extract an object/reference to the
> exact `T` which was provided. Since the "proxy" type's semantics are part
> of its type declaration (defined by `W`), it would be easy to specialize
> the interface for reference proxies vs. value proxies.
>
> Alternatively, you could use the `std::function::target` interface for a
> lighter-weight version. But the overall point is the same: a type-erased
> type ought to be able to extract what it was given.
>
> Yes, This is a question worth considering. Luckily, this does not require
reconstructing, we can simply "add" the feature to this solution. Maybe we
can have something like "std::any_cast":
template<class ValueType, class I, class W>
ValueType proxy_cast(const proxy<I, W>& operand);
template<class ValueType, class I, class W>
ValueType proxy_cast(proxy<I, W>& operand);
template<class ValueType, class I, class W>
ValueType proxy_cast(proxy<I, W>&& operand);
template<class ValueType, class I, class W>
const ValueType* proxy_cast(const proxy<I, W>* operand) noexcept;
template<class ValueType, class I, class W>
ValueType* proxy_cast(proxy<I, W>* operand) noexcept;
Implementations may use dynamic_cast for RTTI. To clarify this issue, we
may as well assume there is an invisible "helper" member function in the
class template proxy for the function templates defined above, as is shown
below:
template <class I, class W> requires Wrapper<W>()
class proxy<I, W> {
public:
// ...
template <class T>
T* __cast() {
Abstraction* ptr =
dynamic_cast<Implementation<T>*>(reinterpret_cast<Abstraction*>(data_.get()));
return ptr == nullptr ? nullptr :
reinterpret_cast<T*>(ptr->wrapper_.get());
}
// ...
private:
class Abstraction : public I {
public:
/* ... */
W wrapper_;
};
class Uninitialized : public Abstraction { /* ... */ };
template <class T>
class Implementation : public Abstraction { /* ... */ };
MemoryBlock<sizeof(Uninitialized)> data_;
};
*Prototypes for Wrappers*
>>
>> Class "SharedWrapper" (with shared semantics), class template
>> "DeepWrapper" (with value semantics and SOO feature) and class
>> "DefferedWrapper" (with reference semantics) are designed to meet the
>> Wrapper requirements. Possible implementation is included in the
>> attachments.
>>
>
> "DeepWrapper" is the wrong name. It provides "value semantics". Calling it
> "deep" suggests "deep copying", which is *not* what this provides. It
> should simply be "ValueWrapper". And there should also be a
> "MoveValueWrapper", for use in cases where you want to allow users to
> provide move-only types.
>
>
"DefferedWrapper" is similarly misnamed (though also misspelled). It
> provides "reference semantics", so it is a "ReferenceWrapper". Admittedly,
> we already have a type with that name, but that's another reason not to
> call them "wrappers" at all.
>
> I am sorry about the misspelling. It should be "DeferredWrapper" rather
than "DefferedWrapper". Still, I suggest we need more feedback and discuss
the naming issue later.
> Also, it's not clear what "SharedWrapper" means. Does it copy/move into
> memory owned by the proxy, ala `make_shared`? Can you pass it a
> `shared_ptr<T>` instead of a `T`, so that you can share ownership of the
> object with the proxy? If not, why not? If you're going to allow shared
> ownership of the value, then it is just as reasonable to allow shared
> ownership of the value with objects that *aren't* the proxy.
>
> Like class template std::shared_ptr, "SharedWrapper" is also based on
reference-counting.The differences between the two are:
- std::shared_ptr is based on pointer semantics, whose general layout
requires two unrelated memory block (that is why we prefer to use
"std::make_shared" to avoid "twice memory allocation", and why some people
dislike std::shared_ptr), while SharedWrapper only requires one memory
block maintaining both the reference-count and the value of the concrete
object (rather than a pointer of the concrete object). Thus there is no
conversion between std::shared_ptr and SharedWrapper as they have different
structures.
- std::shared_ptr is type-specific, while SharedWrapper is type-erased.
See the implementation (shared_wrapper.hpp) included in src.zip for more
details.
This is one of the problems of going to a generic mechanism for handling
> semantics: the rabbit hole of possibilities is infinite.
>
> That being said, since the "wrapper" is part of the proxy's template
> declaration, it is possible to have the "wrapper" actually influence the
> proxy's interface. So the "wrapper" could affect the definition of the
> proxy's template constructor from `T`, as well as the template function
> which extracts a pointer/reference to the proxied type. A `SharedWrapper`
> could therefore allow you to pass a `shared_ptr<T>`, as well as allow you
> to extract a `shared_ptr<T>`.
>
--
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/b61a514a-3a2b-4ce5-8bd3-984d547657a0%40isocpp.org.
------=_Part_4459_1857707340.1497427915750
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Tuesday, June 13, 2017 at 11:20:26 PM 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 Tu=
esday, June 13, 2017 at 9:49:43 AM UTC-4, Mingxin Wang wrote:<blockquote cl=
ass=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #cc=
c solid;padding-left:1ex"><div dir=3D"ltr"><div></div><div><font size=3D"6"=
><b>Technical Specifications</b></font></div><div><br></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 expressions are well-forme=
d and have the specific semantics (w denotes a value of type W).</div><div>=
<br></div><div>w.get()</div><div><span style=3D"white-space:pre"> </span>Re=
quires: w is initialized with an object.</div><div><span style=3D"white-spa=
ce:pre"> </span>Effects: acquires the pointer of the wrapped object if ther=
e is one.</div><div><span style=3D"white-space:pre"> </span>Return type: vo=
id*.</div><div><span style=3D"white-space:pre"> </span>Returns: a pointer o=
f the wrapped object.</div><div><br></div></div></blockquote><div><br>This =
seems decidedly thin on details.<br><br>As I understand your design, the &q=
uot;wrapper" provides both the storage for the object/pointer as well =
as determining the semantics relative to that object/pointer. This is not e=
ntirely clear from your technical specifications, but everything else I'=
;m going to say is based on this assumption.<br><br></div></div></blockquot=
e><div>A "Wrapper" is only required to support semantics for addr=
essing (at minimum).</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>First, "wrapper" is a <i>terri=
ble</i> 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&qu=
ot;. It's simply providing the semantics of the type. So "Semantic=
s" seems a much more descriptive name.<br><br></div></div></blockquote=
><div>I think "Semantics" is not as good as "Wrapper". =
As this is a relatively subjective issue, maybe we need more feedback and d=
iscuss about it later.</div><div>=C2=A0</div><blockquote class=3D"gmail_quo=
te" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;paddi=
ng-left: 1ex;"><div dir=3D"ltr"><div>Second, the `get` interface is entirel=
y 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:<br><br></div>=
</div></blockquote><div>There is no particular requirements on "what t=
ype can be used to construct a wrapper". Actually, a type that meets t=
he Wrapper requirements may carry such constraints on its constructors.</di=
v><div>=C2=A0=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>Third, it's not clear who is actually responsible for =
the <i>type erasure</i> part of this. The type `T` provided to a "prox=
y" seems to be erased by the code generated data. But since the "=
wrapper" holds the storage for said `T`, it must also have enough info=
rmation to <i>destroy</i> that object. After all, the "wrapper" m=
ay 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 w=
hen it goes to destroy it.<br><br></div></div></blockquote><div>A Wrapper m=
ay be responsible to destroy an object just as std::any does, and a Proxy i=
s responsible for ACCESSING the object without RTTI.</div><div>=C2=A0</div>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>This mea=
ns that any "wrapper" that actually owns a `T` must <i>itself</i>=
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.<br><br></div></div>=
</blockquote><div>Not exactly. Double-type-erasure only requires a little m=
ore memory (one-pointer size) and is more compatible. That is why we usuall=
y use "combination instead of inheritance" in architecture design=
ing. Besides, I have ran a performance test for DeepWrapper<Callable<=
void()>, 16u> and std::function<void()> which have the same SOO=
size on my compiler (Windows 7 x64, gcc version 6.3.0 x86_64-posix-seh-rev=
2, Built by MinGW-W64 project, Command: g++.exe -march=3Dcorei7-avx -O2 -m6=
4 -fconcepts -std=3Dc++17), and the result turns out to be positive that De=
epWrapper<Callable<void()>, 16u> is more efficient than the imp=
lementation of std::function<void()> most of the time because there i=
s 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):</div><div><br></div><p class=3D"separator" style=3D"t=
ext-align: center; clear: both;"><a imageanchor=3D"1" href=3D"https://lh3.g=
oogleusercontent.com/-_gjJzdIwPp4/WUDlTwYKtUI/AAAAAAAAAE8/Vl08JuotE1cfLt6bR=
yWB7HQtNeWmkqKBgCLcBGAs/s1600/Untitled.png" style=3D"margin-left: 1em; marg=
in-right: 1em;"><img src=3D"https://lh3.googleusercontent.com/-_gjJzdIwPp4/=
WUDlTwYKtUI/AAAAAAAAAE8/Vl08JuotE1cfLt6bRyWB7HQtNeWmkqKBgCLcBGAs/s320/Untit=
led.png" border=3D"0" style=3D"" width=3D"320" height=3D"212"></a></p><div>=
<br></div><p class=3D"separator" style=3D"text-align: center; clear: both;"=
><a imageanchor=3D"1" href=3D"https://lh3.googleusercontent.com/-mDVlwZYpOg=
M/WUDlbYCiEBI/AAAAAAAAAFA/GNdldWXiF6MjcH54bljg9OaUSJFMQDiwACLcBGAs/s1600/Un=
titled.png" style=3D"margin-left: 1em; margin-right: 1em;"><img src=3D"http=
s://lh3.googleusercontent.com/-mDVlwZYpOgM/WUDlbYCiEBI/AAAAAAAAAFA/GNdldWXi=
F6MjcH54bljg9OaUSJFMQDiwACLcBGAs/s320/Untitled.png" border=3D"0" style=3D""=
width=3D"320" height=3D"212"></a></p><div><br></div><p class=3D"separator"=
style=3D"text-align: center; clear: both;"><a imageanchor=3D"1" href=3D"ht=
tps://lh3.googleusercontent.com/-a7Z1kFHkaEA/WUDliSb7vOI/AAAAAAAAAFE/YvcQfq=
vOGL0AZw4i_aLW7wLZBoHbhOVFwCLcBGAs/s1600/Untitled.png" style=3D"margin-left=
: 1em; margin-right: 1em;"><img src=3D"https://lh3.googleusercontent.com/-a=
7Z1kFHkaEA/WUDliSb7vOI/AAAAAAAAAFE/YvcQfqvOGL0AZw4i_aLW7wLZBoHbhOVFwCLcBGAs=
/s320/Untitled.png" border=3D"0" style=3D"" width=3D"320" height=3D"213"></=
a></p><div>=C2=A0<br></div><blockquote class=3D"gmail_quote" style=3D"margi=
n: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><di=
v dir=3D"ltr"><div>Now, you might be able to work around this problem. You =
could design it so that the "wrapper" explicitly advertises in it=
s interface that it is an "owning" wrapper, such that it needs to=
be provided with the un-erased `T` when the "proxy" is being des=
troyed. But that's a much more complex interface than you have outlined=
here.</div></div></blockquote><div><br></div><div>This is not always neces=
sary, especially when W is a trivial type.</div><div>=C2=A0</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></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></div><div><font size=3D"=
4"><b>Class template proxy</b></font></div><div><br></div><div>Expression &=
quot;proxy<I, W>" is a well-formed type if I is a pure virtual c=
lass (without a virtual destructor) and W is a type meets the Wrapper requi=
rements defined above.</div><div><br></div><div>"proxy<I, W>&quo=
t; is MoveConstructible if W is MoveConstructible, while "proxy<I, =
W>" is CopyConstructible if W is CopyConstructible.</div><div><br><=
/div><div>Providing p is a value of "proxy<I, W>" and i is =
a pointer of I, "p.f(args...)" shall be a valid expression if &qu=
ot;(*i).f(args...)" is a valid expression, where f is any valid functi=
on name (including operator overloads) and "args..." is any valid=
combination of values of any type.</div></div></blockquote><div><br>As pre=
viously mentioned, the "proxy" ought to provide `any_cast`-like f=
unctionality. That is, the ability to extract an object/reference to the ex=
act `T` which was provided. Since the "proxy" type's semantic=
s are part of its type declaration (defined by `W`), it would be easy to sp=
ecialize the interface for reference proxies vs. value proxies.<br><br>Alte=
rnatively, you could use the `std::function::target` interface for a lighte=
r-weight version. But the overall point is the same: a type-erased type oug=
ht to be able to extract what it was given.<br><br></div></div></blockquote=
><div>Yes, This is a question worth considering. Luckily, this does not req=
uire reconstructing, we can simply "add" the feature to this solu=
tion. Maybe we can have something like "std::any_cast":</div><div=
><br></div><div><div class=3D"prettyprint" style=3D"border: 1px solid rgb(1=
87, 187, 187); word-wrap: break-word; background-color: rgb(250, 250, 250);=
"><code class=3D"prettyprint"><div class=3D"subprettyprint"><font color=3D"=
#660066"><div class=3D"subprettyprint">template<class ValueType, class I=
, class W></div><div class=3D"subprettyprint">ValueType proxy_cast(const=
proxy<I, W>& operand);</div><div class=3D"subprettyprint"><br></=
div><div class=3D"subprettyprint">template<class ValueType, class I, cla=
ss W></div><div class=3D"subprettyprint">ValueType proxy_cast(proxy<I=
, W>& operand);</div><div class=3D"subprettyprint"><br></div><div cl=
ass=3D"subprettyprint">template<class ValueType, class I, class W></d=
iv><div class=3D"subprettyprint">ValueType proxy_cast(proxy<I, W>&=
;& operand);</div><div class=3D"subprettyprint"><br></div><div class=3D=
"subprettyprint">template<class ValueType, class I, class W></div><di=
v class=3D"subprettyprint">const ValueType* proxy_cast(const proxy<I, W&=
gt;* operand) noexcept;</div><div class=3D"subprettyprint"><br></div><div c=
lass=3D"subprettyprint">template<class ValueType, class I, class W></=
div><div class=3D"subprettyprint">ValueType* proxy_cast(proxy<I, W>* =
operand) noexcept;</div></font></div></code></div><br>Implementations may u=
se dynamic_cast for RTTI. To clarify this issue, we may as well assume ther=
e is an invisible "helper" member function in the class template =
proxy for the function templates defined above, as is shown below:</div><di=
v><br></div><div><div class=3D"prettyprint" style=3D"border: 1px solid rgb(=
187, 187, 187); word-wrap: break-word; background-color: rgb(250, 250, 250)=
;"><code class=3D"prettyprint"><div class=3D"subprettyprint"><div class=3D"=
subprettyprint">template <class I, class W> requires Wrapper<W>=
()</div><div class=3D"subprettyprint">class proxy<I, W> {</div><div c=
lass=3D"subprettyprint">=C2=A0public:</div><div class=3D"subprettyprint">=
=C2=A0 // ...</div><div class=3D"subprettyprint">=C2=A0 template <class =
T></div><div class=3D"subprettyprint">=C2=A0 T* __cast() {</div><div cla=
ss=3D"subprettyprint">=C2=A0 =C2=A0 Abstraction* ptr =3D dynamic_cast<Im=
plementation<T>*>(reinterpret_cast<Abstraction*>(data_.get()=
));</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 return ptr =3D=3D null=
ptr ? nullptr : reinterpret_cast<T*>(ptr->wrapper_.get());</div><d=
iv class=3D"subprettyprint">=C2=A0 }</div><div class=3D"subprettyprint">=C2=
=A0 // ...</div><div class=3D"subprettyprint">=C2=A0=C2=A0</div><div class=
=3D"subprettyprint">=C2=A0private:</div><div class=3D"subprettyprint">=C2=
=A0 class Abstraction : public I {</div><div class=3D"subprettyprint">=C2=
=A0 =C2=A0public:</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 /* ... *=
/</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 W wrapper_;</div><div cl=
ass=3D"subprettyprint">=C2=A0 };</div><div class=3D"subprettyprint">=C2=A0=
=C2=A0</div><div class=3D"subprettyprint">=C2=A0 class Uninitialized : publ=
ic Abstraction { /* ... */ };</div><div class=3D"subprettyprint">=C2=A0=C2=
=A0</div><div class=3D"subprettyprint">=C2=A0 template <class T></div=
><div class=3D"subprettyprint">=C2=A0 class Implementation : public Abstrac=
tion { /* ... */ };</div><div class=3D"subprettyprint">=C2=A0=C2=A0</div><d=
iv class=3D"subprettyprint">=C2=A0 MemoryBlock<sizeof(Uninitialized)>=
data_;</div><div class=3D"subprettyprint">};</div></div></code></div></div=
><div><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"lt=
r"><div></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=
></div><div><b><font size=3D"4">Prototypes for Wrappers</font></b></div><di=
v><br></div><div>Class "SharedWrapper" (with shared semantics), c=
lass template "DeepWrapper" (with value semantics and SOO feature=
) and class "DefferedWrapper" (with reference semantics) are desi=
gned to meet the Wrapper requirements. Possible implementation is included =
in the attachments.</div></div></blockquote><div><br>"DeepWrapper"=
; is the wrong name. It provides "value semantics". Calling it &q=
uot;deep" suggests "deep copying", which is <i>not</i> what =
this provides. It should simply be "ValueWrapper". And there shou=
ld also be a "MoveValueWrapper", for use in cases where you want =
to allow users to provide move-only types.<br>=C2=A0<br></div></div></block=
quote><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8=
ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>&q=
uot;DefferedWrapper" is similarly misnamed (though also misspelled). I=
t provides "reference semantics", so it is a "ReferenceWrapp=
er". Admittedly, we already have a type with that name, but that's=
another reason not to call them "wrappers" at all.<br><br></div>=
</div></blockquote><div>I am sorry about the misspelling. It should be &quo=
t;DeferredWrapper" rather than "DefferedWrapper". Still, I s=
uggest we need more feedback and discuss the naming issue later.</div><div>=
=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-lef=
t: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><=
div>Also, it's not clear what "SharedWrapper" means. Does it =
copy/move into memory owned by the proxy, ala `make_shared`? Can you pass i=
t a `shared_ptr<T>` instead of a `T`, so that you can share ownership=
of the object with the proxy? If not, why not? If you're going to allo=
w shared ownership of the value, then it is just as reasonable to allow sha=
red ownership of the value with objects that <i>aren't</i> the proxy.<b=
r><br></div></div></blockquote><div>Like class template std::shared_ptr, &q=
uot;SharedWrapper" is also based on reference-counting.The differences=
between the two are:</div><div><ul><li><span style=3D"line-height: normal;=
">std::shared_ptr is based on pointer semantics, whose general layout requi=
res two unrelated memory block (that is why we prefer to use "std::mak=
e_shared" to avoid "twice memory allocation", and why some p=
eople dislike std::shared_ptr), while SharedWrapper only requires one memor=
y block maintaining both the reference-count and the value of the concrete =
object (rather than a pointer of the concrete object). Thus there is no con=
version between std::shared_ptr and SharedWrapper as they have different st=
ructures.</span></li><li><span style=3D"line-height: normal;">std::shared_p=
tr is type-specific, while SharedWrapper is type-erased.</span></li></ul>Se=
e the implementation (shared_wrapper.hpp) included in src.zip for more deta=
ils.</div><div><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> This is one of the problems of going to a generic mechani=
sm for=20
handling semantics: the rabbit hole of possibilities is infinite.<br><br>Th=
at being said, since the "wrapper" is part of the proxy's tem=
plate declaration, it is possible to have the "wrapper" actually =
influence the proxy's interface. So the "wrapper" could affec=
t the definition of the proxy's template constructor from `T`, as well =
as the template function which extracts a pointer/reference to the proxied =
type. A `SharedWrapper` could therefore allow you to pass a `shared_ptr<=
T>`, as well as allow you to extract a `shared_ptr<T>`.<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" 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/b61a514a-3a2b-4ce5-8bd3-984d547657a0%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/b61a514a-3a2b-4ce5-8bd3-984d547657a0=
%40isocpp.org</a>.<br />
------=_Part_4459_1857707340.1497427915750--
------=_Part_4458_399239291.1497427915749--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Wed, 14 Jun 2017 10:13:47 -0700 (PDT)
Raw View
------=_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 "wrapper" 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'm going to say is based =
on this assumption.<br><br></div></div></blockquote><div>A "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, "wrapper" is a <i>terrible</i> name for =
this concept. It isn't "wrapping" anything; it's providin=
g storage and specifying the semantics of the proxy (which also isn't a=
particularly good name). That isn't "wrapping". It's sim=
ply providing the semantics of the type. So "Semantics" seems a m=
uch more descriptive name.<br><br></div></div></blockquote><div>I think &qu=
ot;Semantics" is not as good as "Wrapper". 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 "wrapper" 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 "Wrapper requirements&q=
uot; don't mention it. Furthermore:<br><br></div></div></blockquote><di=
v>There is no particular requirements on "what type can be used to con=
struct a wrapper". 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 "wrapper"=
; is <i>constructible</i> from a reference to some non-zero set of types, y=
es?<br><br>Also, if important aspects of the "wrapper"'s inte=
rface are not "requirements", 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's not cle=
ar who is actually responsible for the <i>type erasure</i> part of this. Th=
e type `T` provided to a "proxy" seems to be erased by the code g=
enerated data. But since the "wrapper" holds the storage for said=
`T`, it must also have enough information to <i>destroy</i> that object. A=
fter all, the "wrapper" may copy/move from the given `T` into new=
storage for that `T`. Which means that the "wrapper" 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 "wrapper" 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 "efficien=
cy" as "performance", OK. But it is also misleading, since &=
quot;efficiency" 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's not clear what you mean by "is more compatibl=
e". With what would it be "compatible"?<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 "combination instead of inheritance" in architecture =
designing. Besides, I have ran a performance test for DeepWrapper<Callab=
le<void()>, 16u> and std::function<void()></div></div></bloc=
kquote><div><br>That's the wrong test. A proper test would be between &=
quot;DeepWrapper" and a hand-crafted equivalent. `std::function` has a=
dditional stuff in it that has no analog in the "DeepWrapper" 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<Callable<void()>, 16u> is more efficient =
than the implementation of std::function<void()> most of the time bec=
ause 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 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 "wrapper&q=
uot; explicitly advertises in its interface that it is an "owning"=
; wrapper, such that it needs to be provided with the un-erased `T` when th=
e "proxy" is being destroyed. But that'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'm not sure I understand your point h=
ere.<br><br>As I understand this feature, the whole point of "wrappers=
" 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't needed for trivial &q=
uot;wrappers"? Many of the non-trivial cases could really use that int=
erface, and support for those cases is exactly why "wrapper" exis=
ts.<br><br>Also, we'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's special member =
functions. The destructor of the Wrapper is what calls the destructor of th=
e "wrapped" object. If the Wrapper is copyable, then its copy con=
structor is what copies the "wrapped" type. Etc.<br><br>This is w=
hy the term "wrapper" 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 "wrapper" 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'll rename the type, so it's clear when I'm talking about o=
ne vs. the other.<br><br>A "semantic" is an object which provides=
storage and semantics for a value. However, it doesn'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 "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" mus=
t be template functions. Note that in most cases, the "semantic" =
interfaces don't need to be provided with the `T` object itself; just t=
he `T` that is being utilized.<br><br>The available "semantic" in=
terfaces would be:<br><br>* `bind`: Stores a given `T`. Required, though it=
can refuse to accept `T`s that don'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 "semantic" into an unbound "semantic". If=
this is deleted, then the proxy is non-copyable. If this is not present, t=
hen the proxy will just copy the "semantic" object directly.<br>*=
`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 th=
e proxy will just move the "semantic" 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 "semantic" 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 "ValueSemantic" doesn'=
;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" 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--
.
Author: =?UTF-8?Q?Klaim_=2D_Jo=C3=ABl_Lamotte?= <mjklaim@gmail.com>
Date: Fri, 16 Jun 2017 18:16:22 +0200
Raw View
--94eb2c12295a32b37f0552161b0f
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On 13 June 2017 at 15:49, Mingxin Wang <wmx16835vv@163.com> wrote:
> *Class template proxy*
>
> Expression "proxy<I, W>" is a well-formed type if I is a pure virtual
> class (without a virtual destructor) and W is a type meets the Wrapper
> requirements defined above.
>
> "proxy<I, W>" is MoveConstructible if W is MoveConstructible, while
> "proxy<I, W>" is CopyConstructible if W is CopyConstructible.
>
> Providing p is a value of "proxy<I, W>" and i is a pointer of I,
> "p.f(args...)" shall be a valid expression if "(*i).f(args...)" is a vali=
d
> expression, where f is any valid function name (including operator
> overloads) and "args..." is any valid combination of values of any type.
>
Could you clarify: if a system want to take anything that have the
interface described by I, how should it
some_system.take_it_and_do_something( ??? object );
I believe this would be really too restrictive:
some_system.take_it_and_do_something( proxy<I,W> object );
Because it would force the user code to use only one ownership strategy for
anything passed to this function.
In my experience, I want the system to ignore if it will own or not the
object, let the user decide, as long as
the proxy is usable as a normal object.
In my opinion, the fact that the real object is shared or not should not be
imposed by the system consuming the proxy.
Jo=C3=ABl Lamotte
--=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/CAOU91OO5iXoZ1Hf%3Dq7Ux%3DBuH9C_fFfUz_%3Dxt3DWEA=
JbrhzOzhA%40mail.gmail.com.
--94eb2c12295a32b37f0552161b0f
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div class=3D"gmail_extra"><br><div class=3D"gmail_quote">=
On 13 June 2017 at 15:49, Mingxin Wang <span dir=3D"ltr"><<a href=3D"mai=
lto:wmx16835vv@163.com" target=3D"_blank">wmx16835vv@163.com</a>></span>=
wrote:<br><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><font siz=
e=3D"4"><b>Class template proxy</b></font></div><div><br></div><div>Express=
ion "proxy<I, W>" is a well-formed type if I is a pure virt=
ual class (without a virtual destructor) and W is a type meets the Wrapper =
requirements defined above.</div><div><br></div><div>"proxy<I, W>=
;" is MoveConstructible if W is MoveConstructible, while "proxy&l=
t;I, W>" is CopyConstructible if W is CopyConstructible.</div><div>=
<br></div><div>Providing p is a value of "proxy<I, W>" and =
i is a pointer of I, "p.f(args...)" shall be a valid expression i=
f "(*i).f(args...)" is a valid expression, where f is any valid f=
unction name (including operator overloads) and "args..." is any =
valid combination of values of any type.</div><div></div></blockquote></div=
><br><br></div><div class=3D"gmail_extra">Could you clarify: if a system wa=
nt to take anything that have the interface described by I, how should it=
=C2=A0</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra"=
>=C2=A0 =C2=A0 some_system.take_it_and_do_something( ??? object );</div><di=
v class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">I believe this=
would be really too restrictive:</div><div class=3D"gmail_extra"><br></div=
><div class=3D"gmail_extra">=C2=A0 =C2=A0 some_system.take_it_and_do_someth=
ing( proxy<I,W> object );</div><div class=3D"gmail_extra"><br></div><=
div class=3D"gmail_extra">Because it would force the user code to use only =
one ownership strategy for anything passed to this function.</div><div clas=
s=3D"gmail_extra">In my experience, I want the system to ignore if it will =
own or not the object, let the user decide, as long as</div><div class=3D"g=
mail_extra">the proxy is usable as a normal object.</div><div class=3D"gmai=
l_extra">In my opinion, the fact that the real object is shared or not shou=
ld not be imposed by the system consuming the proxy.</div><div class=3D"gma=
il_extra"><br></div><div class=3D"gmail_extra">Jo=C3=ABl Lamotte</div><div =
class=3D"gmail_extra"><br></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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/CAOU91OO5iXoZ1Hf%3Dq7Ux%3DBuH9C_fFfUz=
_%3Dxt3DWEAJbrhzOzhA%40mail.gmail.com?utm_medium=3Demail&utm_source=3Dfoote=
r">https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91OO5iX=
oZ1Hf%3Dq7Ux%3DBuH9C_fFfUz_%3Dxt3DWEAJbrhzOzhA%40mail.gmail.com</a>.<br />
--94eb2c12295a32b37f0552161b0f--
.
Author: Mingxin Wang <wmx16835vv@163.com>
Date: Fri, 16 Jun 2017 18:07:13 -0700 (PDT)
Raw View
------=_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 "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, bu=
t everything else I'm going to say is based on this assumption.<br><br>=
</div></div></blockquote><div>A "Wrapper" 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, "wrapper&q=
uot; is a <i>terrible</i> name for this concept. It isn't "wrappin=
g" anything; it's providing storage and specifying the semantics o=
f 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.<br><br></div>=
</div></blockquote><div>I think "Semantics" is not as good as &qu=
ot;Wrapper". 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 "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:<=
br><br></div></div></blockquote><div>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 cons=
tructors.</div></div></blockquote><div><br>Which means that there is a requ=
irement that the "wrapper" is <i>constructible</i> from a referen=
ce to some non-zero set of types, yes?<br><br>Also, if important aspects of=
the "wrapper"'s interface are not "requirements", =
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<I, W> 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&&) { { W(std::forward<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<void()>{...}) 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's not clear who is actually re=
sponsible for the <i>type erasure</i> part of this. The type `T` provided t=
o a "proxy" seems to be erased by the code generated data. But si=
nce the "wrapper" holds the storage for said `T`, it must also ha=
ve enough information to <i>destroy</i> that object. After all, the "w=
rapper" 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.<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 "wrapper" 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 "efficiency" as "perf=
ormance", 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 <i>spirit</i> of it (that a user should gain =
nothing from hand-writing their own) is still violated.<br><br>Also, it'=
;s not clear what you mean by "is more compatible". With what wou=
ld it be "compatible"?<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 "combi=
nation instead of inheritance" in architecture designing. Besides, I h=
ave ran a performance test for DeepWrapper<Callable<void()>, 16u&g=
t; and std::function<void()></div></div></blockquote><div><br>That=
9;s the wrong test. A proper test would be between "DeepWrapper" =
and a hand-crafted equivalent. `std::function` has additional stuff in it t=
hat has no analog in the "DeepWrapper" proxy.<br><br></div></div>=
</blockquote><div>I am sorry that "DeepWrapper" should be "D=
eepProxy" (which is defined in src.zip/proxy.hpp, DeepProxy<I, N>=
; =3D=3D proxy<I, DeepWrapper<N>>). The performance test is act=
ually between DeepProxy<Callable<void()>, 16u> and std::functio=
n<void()>.</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 "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 implementa=
tions included in src.zip (DeferredWrapper and SharedWrapper). The reason w=
hy I only tested DeepProxy<Callable<void()>, 16u> and std::func=
tion<void()> 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<Callable<void()>, 16u> is more efficient than the implementa=
tion of std::function<void()> most of the time because there is an ex=
tra addressing operation for the virtual table for std::function<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 "wrapper" explicitly adv=
ertises in its interface that it is an "owning" wrapper, such tha=
t 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.</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'm not sure I understand your point here.<br><br>As I un=
derstand this feature, the whole point of "wrappers" 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'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.<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 "DeepWrapp=
er", 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'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's special member functions. T=
he destructor of the Wrapper is what calls the destructor of the "wrap=
ped" object. If the Wrapper is copyable, then its copy constructor is =
what copies the "wrapped" type. Etc.<br><br>This is why the term =
"wrapper" 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 "wrapper" 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'll rename the type, so it's clear when I'm talking abo=
ut one vs. the other.<br><br>A "semantic" is an object which prov=
ides storage and semantics for a value. However, it doesn'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 "semantic" provides.=
The `T`-specific derived classes would implement versions that call the &q=
uot;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&qu=
ot; interfaces don't need to be provided with the `T` object itself; ju=
st the `T` that is being utilized.<br><br>The available "semantic"=
; interfaces would be:<br><br>* `bind`: Stores a given `T`. Required, thoug=
h it can refuse to accept `T`s that don'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 "semantic" into an unbound "semantic"=
.. If this is deleted, then the proxy is non-copyable. If this is not presen=
t, then the proxy will just copy the "semantic" object directly.<=
br>* `move`: Moves the `T` from one "semantic" 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 "semantic" 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 "semantic" 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 "ValueSemantic" doesn=
'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" 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--
.
Author: Mingxin Wang <wmx16835vv@163.com>
Date: Fri, 16 Jun 2017 18:55:15 -0700 (PDT)
Raw View
------=_Part_1833_161804354.1497664515735
Content-Type: multipart/alternative;
boundary="----=_Part_1834_1629482302.1497664515736"
------=_Part_1834_1629482302.1497664515736
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On Saturday, June 17, 2017 at 12:16:24 AM UTC+8, Klaim - Jo=C3=ABl Lamotte =
wrote:
>
>
> On 13 June 2017 at 15:49, Mingxin Wang <wmx16...@163.com <javascript:>>=
=20
> wrote:
>
>> *Class template proxy*
>>
>> Expression "proxy<I, W>" is a well-formed type if I is a pure virtual=20
>> class (without a virtual destructor) and W is a type meets the Wrapper=
=20
>> requirements defined above.
>>
>> "proxy<I, W>" is MoveConstructible if W is MoveConstructible, while=20
>> "proxy<I, W>" is CopyConstructible if W is CopyConstructible.
>>
>> Providing p is a value of "proxy<I, W>" and i is a pointer of I,=20
>> "p.f(args...)" shall be a valid expression if "(*i).f(args...)" is a val=
id=20
>> expression, where f is any valid function name (including operator=20
>> overloads) and "args..." is any valid combination of values of any type.
>>
>
>
> Could you clarify: if a system want to take anything that have the=20
> interface described by I, how should it=20
>
> some_system.take_it_and_do_something( ??? object );
>
> I believe this would be really too restrictive:
>
> some_system.take_it_and_do_something( proxy<I,W> object );
>
> Because it would force the user code to use only one ownership strategy=
=20
> for anything passed to this function.
> In my experience, I want the system to ignore if it will own or not the=
=20
> object, let the user decide, as long as
> the proxy is usable as a normal object.
> In my opinion, the fact that the real object is shared or not should not=
=20
> be imposed by the system consuming the proxy.
>
Any type that has the required expressions specified by I and can be used=
=20
to construct the wrapper specified by W, can be implicitly convertible to=
=20
proxy<I, W>. It is true that users are responsible to specify ownership=20
strategies in type declarations, but this only happens in a certain=20
context. When there is no need for polymorphism, I prefer to declare a=20
function as a template. Here is an example use case for proxies and=20
templates:
template <class Task =3D SharedProxy<Callable<void()>>>
class CompositTask {
public:
template <class... Args>
void emplace(Args&&... args) {
data_.emplace_back(std::forward<Args>(args)...);
}
void operator()() {
for (auto& t : data_) t();
}
private:
std::vector<Task> data_;
};
Proxy usually works well with templates. The code above is a class template=
=20
with a default type, which satisfies the requirements specified by=20
Callable<void()>. Providing there is another function declared as follows:
void submit_a_task(DeepProxy<Callable<void()>> task);
The following code is well-formed for client code:
CompositTask<> c;
c.emplace([] { puts("Lambda Expression"); });
c.emplace(std::bind([](const char* x) { puts(x); }, "Bind Expression"));
c.emplace(c);
submit_a_task(c);
Every conversion is done implicitly.
=20
>
> Jo=C3=ABl Lamotte
>
>
Mingxin Wang
--=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/1edb48fd-0d4c-478b-816b-6cd9b21b4f0c%40isocpp.or=
g.
------=_Part_1834_1629482302.1497664515736
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Saturday, June 17, 2017 at 12:16:24 AM UTC+8, Klaim - J=
o=C3=ABl Lamotte wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0=
;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div di=
r=3D"ltr"><div><br><div class=3D"gmail_quote">On 13 June 2017 at 15:49, Min=
gxin Wang <span dir=3D"ltr"><<a href=3D"javascript:" target=3D"_blank" g=
df-obfuscated-mailto=3D"2L3Zqt6cAAAJ" rel=3D"nofollow" onmousedown=3D"this.=
href=3D'javascript:';return true;" onclick=3D"this.href=3D'java=
script:';return true;">wmx16...@163.com</a>></span> wrote:<br><block=
quote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1=
px solid rgb(204,204,204);padding-left:1ex"><div><font size=3D"4"><b>Class =
template proxy</b></font></div><div><br></div><div>Expression "proxy&l=
t;I, W>" is a well-formed type if I is a pure virtual class (withou=
t a virtual destructor) and W is a type meets the Wrapper requirements defi=
ned above.</div><div><br></div><div>"proxy<I, W>" is MoveCo=
nstructible if W is MoveConstructible, while "proxy<I, W>" =
is CopyConstructible if W is CopyConstructible.</div><div><br></div><div>Pr=
oviding p is a value of "proxy<I, W>" and i is a pointer of=
I, "p.f(args...)" shall be a valid expression if "(*i).f(ar=
gs...)" is a valid expression, where f is any valid function name (inc=
luding operator overloads) and "args..." is any valid combination=
of values of any type.</div><div></div></blockquote></div><br><br></div><d=
iv>Could you clarify: if a system want to take anything that have the inter=
face described by I, how should it=C2=A0</div><div><br></div><div>=C2=A0 =
=C2=A0 some_system.take_it_and_do_<wbr>something( ??? object );</div><div><=
br></div><div>I believe this would be really too restrictive:</div><div><br=
></div><div>=C2=A0 =C2=A0 some_system.take_it_and_do_<wbr>something( proxy&=
lt;I,W> object );</div><div><br></div><div>Because it would force the us=
er code to use only one ownership strategy for anything passed to this func=
tion.</div><div>In my experience, I want the system to ignore if it will ow=
n or not the object, let the user decide, as long as</div><div>the proxy is=
usable as a normal object.</div><div>In my opinion, the fact that the real=
object is shared or not should not be imposed by the system consuming the =
proxy.</div></div></blockquote><div><br></div><div>Any type that has the re=
quired expressions specified by I and can be used to construct the wrapper =
specified by W, can be implicitly convertible to proxy<I, W>. It is t=
rue that users are responsible to specify ownership strategies in type decl=
arations, but this only happens in a certain context. When there is no need=
for polymorphism, I prefer to declare a function as a template. Here is an=
example use case for proxies and templates:</div><div><br></div><div><div =
class=3D"prettyprint" style=3D"border: 1px solid rgb(187, 187, 187); word-w=
rap: break-word; background-color: rgb(250, 250, 250);"><code class=3D"pret=
typrint"><div class=3D"subprettyprint"><font color=3D"#660066"><div class=
=3D"subprettyprint">template <class Task =3D SharedProxy<Callable<=
void()>>></div><div class=3D"subprettyprint">class CompositTask {<=
/div><div class=3D"subprettyprint">=C2=A0public:</div><div class=3D"subpret=
typrint">=C2=A0 template <class... Args></div><div class=3D"subpretty=
print">=C2=A0 void emplace(Args&&... args) {</div><div class=3D"sub=
prettyprint">=C2=A0 =C2=A0 data_.emplace_back(std::forward<Args>(args=
)...);</div><div class=3D"subprettyprint">=C2=A0 }</div><div class=3D"subpr=
ettyprint"><br></div><div class=3D"subprettyprint">=C2=A0 void operator()()=
{</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 for (auto& t : data=
_) t();</div><div class=3D"subprettyprint">=C2=A0 }</div><div class=3D"subp=
rettyprint"><br></div><div class=3D"subprettyprint">=C2=A0private:</div><di=
v class=3D"subprettyprint">=C2=A0 std::vector<Task> data_;</div><div =
class=3D"subprettyprint">};</div></font></div></code></div><br>Proxy usuall=
y works well with templates. The code above is a class template with a defa=
ult type, which satisfies the requirements specified by Callable<void()&=
gt;. Providing there is another function declared as follows:</div><div><br=
></div><div><div class=3D"prettyprint" style=3D"border: 1px solid rgb(187, =
187, 187); word-wrap: break-word; background-color: rgb(250, 250, 250);"><c=
ode class=3D"prettyprint"><div class=3D"subprettyprint"><font color=3D"#660=
066">void submit_a_task(DeepProxy<Callable<void()>> task);</fon=
t><br></div></code></div><br>The following code is well-formed for client c=
ode:</div><div><br></div><div><div class=3D"prettyprint" style=3D"border: 1=
px solid rgb(187, 187, 187); word-wrap: break-word; background-color: rgb(2=
50, 250, 250);"><code class=3D"prettyprint"><div class=3D"subprettyprint"><=
div class=3D"subprettyprint">CompositTask<> c;</div><div class=3D"sub=
prettyprint">c.emplace([] { puts("Lambda Expression"); });</div><=
div class=3D"subprettyprint">c.emplace(std::bind([](const char* x) { puts(x=
); }, "Bind Expression"));</div><div class=3D"subprettyprint">c.e=
mplace(c);</div><div class=3D"subprettyprint">submit_a_task(c);</div></div>=
</code></div><br>Every conversion is done implicitly.</div><div>=C2=A0</div=
><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bo=
rder-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></d=
iv><div>Jo=C3=ABl Lamotte</div><div><br></div></div></blockquote><div><br><=
/div><div>Mingxin Wang</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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/1edb48fd-0d4c-478b-816b-6cd9b21b4f0c%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/1edb48fd-0d4c-478b-816b-6cd9b21b4f0c=
%40isocpp.org</a>.<br />
------=_Part_1834_1629482302.1497664515736--
------=_Part_1833_161804354.1497664515735--
.
Author: Mingxin Wang <wmx16835vv@163.com>
Date: Wed, 21 Jun 2017 20:02:35 -0700 (PDT)
Raw View
------=_Part_5523_222890770.1498100555430
Content-Type: multipart/alternative;
boundary="----=_Part_5524_809515705.1498100555430"
------=_Part_5524_809515705.1498100555430
Content-Type: text/plain; charset="UTF-8"
I designed another general wrapper type yesterday, which is intended for
small and trivial types, as is shown below:
template <std::size_t SIZE>
class TrivialWrapper {
public:
/* Constructors */
template <class T>
TrivialWrapper(T&& data) requires
!std::is_same<std::remove_cv_t<std::remove_reference_t<T>>,
TrivialWrapper>::value &&
std::is_trivial<std::remove_cv_t<std::remove_reference_t<T>>>::value
&&
(sizeof(std::remove_cv_t<std::remove_reference_t<T>>) <= SIZE) {
memcpy(data_.get(), &data,
sizeof(std::remove_cv_t<std::remove_reference_t<T>>));
}
TrivialWrapper() = default;
TrivialWrapper(const TrivialWrapper&) = default;
TrivialWrapper(TrivialWrapper&&) = default;
TrivialWrapper& operator=(const TrivialWrapper& rhs) = default;
TrivialWrapper& operator=(TrivialWrapper&&) = default;
/* Meets the Wrapper requirements */
void* get() {
// The address of the wrapped object is calculated with a constant
offset
return data_.get();
}
private:
MemoryBlock<SIZE> data_;
};
I am wondering if these design for wrappers (DeferredWrapper, DeepWrapper,
SharedWrapper and TrivialWrapper) are adequate to be added to the standard
(discard of naming).
I am looking forward to your comments and suggestions!
Thank you!
Mingxin Wang
--
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/d6138434-59ef-42d7-82d8-f2934c6f3bfb%40isocpp.org.
------=_Part_5524_809515705.1498100555430
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div>I designed another general wrapper type yesterday, wh=
ich is intended for small and trivial types, as is shown below:<br></div><d=
iv><br></div><div><div class=3D"prettyprint" style=3D"border: 1px solid rgb=
(187, 187, 187); word-wrap: break-word; background-color: rgb(250, 250, 250=
);"><code class=3D"prettyprint"><div class=3D"subprettyprint"><font color=
=3D"#660066"><div class=3D"subprettyprint">template <std::size_t SIZE>=
;</div><div class=3D"subprettyprint">class TrivialWrapper {</div><div class=
=3D"subprettyprint">=C2=A0public:</div><div class=3D"subprettyprint">=C2=A0=
/* Constructors */</div><div class=3D"subprettyprint">=C2=A0 template <=
class T></div><div class=3D"subprettyprint">=C2=A0 TrivialWrapper(T&=
& data) requires</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=
=A0 !std::is_same<std::remove_cv_t<std::remove_reference_t<T>&g=
t;, TrivialWrapper>::value &&</div><div class=3D"subprettyprint"=
>=C2=A0 =C2=A0 =C2=A0 std::is_trivial<std::remove_cv_t<std::remove_re=
ference_t<T>>>::value &&</div><div class=3D"subprettypr=
int">=C2=A0 =C2=A0 =C2=A0 (sizeof(std::remove_cv_t<std::remove_reference=
_t<T>>) <=3D SIZE) {</div><div class=3D"subprettyprint">=C2=A0 =
=C2=A0 memcpy(data_.get(), &data, sizeof(std::remove_cv_t<std::remov=
e_reference_t<T>>));</div><div class=3D"subprettyprint">=C2=A0 }</=
div><div class=3D"subprettyprint">=C2=A0 TrivialWrapper() =3D default;</div=
><div class=3D"subprettyprint">=C2=A0 TrivialWrapper(const TrivialWrapper&a=
mp;) =3D default;</div><div class=3D"subprettyprint">=C2=A0 TrivialWrapper(=
TrivialWrapper&&) =3D default;</div><div class=3D"subprettyprint"><=
br></div><div class=3D"subprettyprint">=C2=A0 TrivialWrapper& operator=
=3D(const TrivialWrapper& rhs) =3D default;</div><div class=3D"subprett=
yprint">=C2=A0 TrivialWrapper& operator=3D(TrivialWrapper&&) =
=3D default;</div><div class=3D"subprettyprint"><br></div><div class=3D"sub=
prettyprint">=C2=A0 /* Meets the Wrapper requirements */</div><div class=3D=
"subprettyprint">=C2=A0 void* get() {</div><div class=3D"subprettyprint">=
=C2=A0 =C2=A0 // The address of the wrapped object is calculated with a con=
stant offset</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 return data_.=
get();</div><div class=3D"subprettyprint">=C2=A0 }</div><div class=3D"subpr=
ettyprint"><br></div><div class=3D"subprettyprint">=C2=A0private:</div><div=
class=3D"subprettyprint">=C2=A0 MemoryBlock<SIZE> data_;</div><div c=
lass=3D"subprettyprint">};</div></font></div></code></div><br><br></div><di=
v>I am wondering if these design for wrappers (DeferredWrapper, DeepWrapper=
, SharedWrapper and TrivialWrapper) are adequate to be added to the standar=
d (discard of naming).</div><div><br></div><div>I am looking forward to you=
r comments and suggestions!</div><div><br></div><div>Thank you!</div><div><=
br></div><div>Mingxin Wang</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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/d6138434-59ef-42d7-82d8-f2934c6f3bfb%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d6138434-59ef-42d7-82d8-f2934c6f3bfb=
%40isocpp.org</a>.<br />
------=_Part_5524_809515705.1498100555430--
------=_Part_5523_222890770.1498100555430--
.
Author: Magnus Fromreide <magfr@lysator.liu.se>
Date: Thu, 22 Jun 2017 10:24:45 +0200
Raw View
On Wed, Jun 21, 2017 at 08:02:35PM -0700, Mingxin Wang wrote:
> I designed another general wrapper type yesterday, which is intended for
> small and trivial types, as is shown below:
>
> template <std::size_t SIZE>
> class TrivialWrapper {
> public:
> /* Constructors */
> template <class T>
> TrivialWrapper(T&& data) requires
> !std::is_same<std::remove_cv_t<std::remove_reference_t<T>>,
> TrivialWrapper>::value &&
> std::is_trivial<std::remove_cv_t<std::remove_reference_t<T>>>::value
> &&
> (sizeof(std::remove_cv_t<std::remove_reference_t<T>>) <= SIZE) {
Why remove_cv_t here?
> memcpy(data_.get(), &data,
> sizeof(std::remove_cv_t<std::remove_reference_t<T>>));
> }
> TrivialWrapper() = default;
> TrivialWrapper(const TrivialWrapper&) = default;
> TrivialWrapper(TrivialWrapper&&) = default;
>
> TrivialWrapper& operator=(const TrivialWrapper& rhs) = default;
> TrivialWrapper& operator=(TrivialWrapper&&) = default;
This is generally broken. Consider the folloving trivial data structure:
struct c_list {
struct c_list *next, *prev;
int data;
};
It is a trivial data structure but if you start moving (or copying) it around
with your wrapper things will break badly.
> /* Meets the Wrapper requirements */
> void* get() {
> // The address of the wrapped object is calculated with a constant
> offset
> return data_.get();
> }
>
> private:
> MemoryBlock<SIZE> data_;
> };
>
>
> I am wondering if these design for wrappers (DeferredWrapper, DeepWrapper,
> SharedWrapper and TrivialWrapper) are adequate to be added to the standard
> (discard of naming).
I think you need to put in a lot of more work on why they are generally
useful.
/MF
--
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/20170622082445.GA5007%40noemi.bahnhof.se.
.
Author: Mingxin Wang <wmx16835vv@163.com>
Date: Thu, 22 Jun 2017 02:24:47 -0700 (PDT)
Raw View
------=_Part_707_1375949212.1498123488035
Content-Type: multipart/alternative;
boundary="----=_Part_708_1691306897.1498123488036"
------=_Part_708_1691306897.1498123488036
Content-Type: text/plain; charset="UTF-8"
On Thursday, June 22, 2017 at 4:24:51 PM UTC+8, Magnus Fromreide wrote:
>
> On Wed, Jun 21, 2017 at 08:02:35PM -0700, Mingxin Wang wrote:
> > I designed another general wrapper type yesterday, which is intended for
> > small and trivial types, as is shown below:
> >
> > template <std::size_t SIZE>
> > class TrivialWrapper {
> > public:
> > /* Constructors */
> > template <class T>
> > TrivialWrapper(T&& data) requires
> > !std::is_same<std::remove_cv_t<std::remove_reference_t<T>>,
> > TrivialWrapper>::value &&
> >
> std::is_trivial<std::remove_cv_t<std::remove_reference_t<T>>>::value
> > &&
> > (sizeof(std::remove_cv_t<std::remove_reference_t<T>>) <= SIZE) {
>
> Why remove_cv_t here?
>
> Because T may have const or volatile qualifiers,
"std::remove_cv_t<std::remove_reference_t<T>>" represents the raw type of T.
> > memcpy(data_.get(), &data,
> > sizeof(std::remove_cv_t<std::remove_reference_t<T>>));
> > }
> > TrivialWrapper() = default;
> > TrivialWrapper(const TrivialWrapper&) = default;
> > TrivialWrapper(TrivialWrapper&&) = default;
> >
> > TrivialWrapper& operator=(const TrivialWrapper& rhs) = default;
> > TrivialWrapper& operator=(TrivialWrapper&&) = default;
>
> This is generally broken. Consider the folloving trivial data structure:
>
> struct c_list {
> struct c_list *next, *prev;
> int data;
> };
>
> It is a trivial data structure but if you start moving (or copying) it
> around
> with your wrapper things will break badly.
>
> Actually, I did not see the problem as the following client code is
well-formed:
c_list head;
head.next = nullptr;
head.prev = nullptr;
head.data = 3;
TrivialWrapper<sizeof(c_list)> w(head);
printf("%d\n", static_cast<c_list*>(w.get())->data);
The code above is not elegent as there is a type cast. However, wrapper
types are not directly used in this solution, they are designed for the
proxies. Suppose there is a trivial callable type defined as below:
struct Demo {
void operator()() {
for (int i = 0; i < 10; ++i) {
printf("%d\n", d[i]);
}
}
int d[10];
};
The following cilent code is well-formed:
Demo x;
for (int i = 0; i < 10; ++i) {
x.d[i] = i; /// Initialize x with 0~9
}
proxy<Callable<void()>, TrivialWrapper<sizeof(Demo)>> p1(x), p2; /// p1 and
p2 are proxy types
p2 = p1;
p1(); /// Print 0~9
p2(); /// Print 0~9
When assign p1 to p2, the memory block of p1 is directly copied to p2.
Then, p1 and p2 holds the same value.
Everything a TrivialWrapper can solve can be solved with DeepWrapper. The
differences between the two are the performance and scope of application:
- TrivialWrapper usually has higher runtime performance, because it is
not polymorphic, and
- DeepWrapper usually has a wider scope of application, because it can
be converted from a type of any size and regardless of whether the type is
trivial or not.
I prefer to use TrivialWrapper in performance sensitive cases, and
DeepWrapper is recommended to be used in generality sensitive cases.
> > /* Meets the Wrapper requirements */
> > void* get() {
> > // The address of the wrapped object is calculated with a constant
> > offset
> > return data_.get();
> > }
> >
> > private:
> > MemoryBlock<SIZE> data_;
> > };
> >
> >
> > I am wondering if these design for wrappers (DeferredWrapper,
> DeepWrapper,
> > SharedWrapper and TrivialWrapper) are adequate to be added to the
> standard
> > (discard of naming).
>
> I think you need to put in a lot of more work on why they are generally
> useful.
>
> /MF
>
--
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/f50fbc34-4601-486b-8f56-b3cef5c6f2f3%40isocpp.org.
------=_Part_708_1691306897.1498123488036
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Thursday, June 22, 2017 at 4:24:51 PM UTC+8, Magnus Fro=
mreide wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-le=
ft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">On Wed, Jun 21, 2=
017 at 08:02:35PM -0700, Mingxin Wang wrote:
<br>> I designed another general wrapper type yesterday, which is intend=
ed for=20
<br>> small and trivial types, as is shown below:
<br>>=20
<br>> template <std::size_t SIZE>
<br>> class TrivialWrapper {
<br>> =C2=A0public:
<br>> =C2=A0 /* Constructors */
<br>> =C2=A0 template <class T>
<br>> =C2=A0 TrivialWrapper(T&& data) requires
<br>> =C2=A0 =C2=A0 =C2=A0 !std::is_same<std::remove_cv_<wbr>t<std=
::remove_reference_t<T>>,=20
<br>> TrivialWrapper>::value &&
<br>> =C2=A0 =C2=A0 =C2=A0 std::is_trivial<std::remove_<wbr>cv_t<s=
td::remove_reference_t<<wbr>T>>>::value=20
<br>> &&
<br>> =C2=A0 =C2=A0 =C2=A0 (sizeof(std::remove_cv_t<std::<wbr>remove_=
reference_t<T>>) <=3D SIZE) {
<br>
<br>Why remove_cv_t here?
<br>
<br></blockquote><div>Because T may have const or volatile=C2=A0qualifiers,=
"std::remove_cv_t<std::<wbr>remove_reference_t<T>>" =
represents the raw type of T.</div><div>=C2=A0</div><blockquote class=3D"gm=
ail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc soli=
d;padding-left: 1ex;">> =C2=A0 =C2=A0 memcpy(data_.get(), &data,=20
<br>> sizeof(std::remove_cv_t<std::<wbr>remove_reference_t<T>&g=
t;));
<br>> =C2=A0 }
<br>> =C2=A0 TrivialWrapper() =3D default;
<br>> =C2=A0 TrivialWrapper(const TrivialWrapper&) =3D default;
<br>> =C2=A0 TrivialWrapper(TrivialWrapper&<wbr>&) =3D default;
<br>>=20
<br>> =C2=A0 TrivialWrapper& operator=3D(const TrivialWrapper& r=
hs) =3D default;
<br>> =C2=A0 TrivialWrapper& operator=3D(TrivialWrapper&&) =
=3D default;
<br>
<br>This is generally broken. Consider the folloving trivial data structure=
:
<br>
<br>struct c_list {
<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct c_list *next, *p=
rev;
<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0int data;
<br>};
<br>
<br>It is a trivial data structure but if you start moving (or copying) it =
around
<br>with your wrapper things will break badly.
<br>
<br></blockquote><div>Actually, I did not see the problem as the following =
client code is well-formed:</div><div><br></div><div><div class=3D"prettypr=
int" style=3D"border: 1px solid rgb(187, 187, 187); word-wrap: break-word; =
background-color: rgb(250, 250, 250);"><code class=3D"prettyprint"><div cla=
ss=3D"subprettyprint"><div class=3D"subprettyprint">c_list head;</div><div =
class=3D"subprettyprint">head.next =3D nullptr;</div><div class=3D"subprett=
yprint">head.prev =3D nullptr;</div><div class=3D"subprettyprint">head.data=
=3D 3;</div><div class=3D"subprettyprint">TrivialWrapper<sizeof(c_list)=
> w(head);</div><div class=3D"subprettyprint">printf("%d\n", s=
tatic_cast<c_list*>(w.get())->data);</div></div></code></div><div>=
<br></div><div>The code above is not elegent as there is a type cast. Howev=
er, wrapper types are not directly used in this solution, they are designed=
for the proxies. Suppose there is a trivial callable type defined as below=
:</div></div><div><br></div><div><div class=3D"prettyprint" style=3D"border=
: 1px solid rgb(187, 187, 187); word-wrap: break-word; background-color: rg=
b(250, 250, 250);"><code class=3D"prettyprint"><div class=3D"subprettyprint=
"><font color=3D"#660066"><div class=3D"subprettyprint">struct Demo {</div>=
<div class=3D"subprettyprint">=C2=A0 void operator()() {</div><div class=3D=
"subprettyprint">=C2=A0 =C2=A0 for (int i =3D 0; i < 10; ++i) {</div><di=
v class=3D"subprettyprint">=C2=A0 =C2=A0 =C2=A0 printf("%d\n", d[=
i]);</div><div class=3D"subprettyprint">=C2=A0 =C2=A0 }</div><div class=3D"=
subprettyprint">=C2=A0 }</div><div class=3D"subprettyprint"><br></div><div =
class=3D"subprettyprint">=C2=A0 int d[10];</div><div class=3D"subprettyprin=
t">};</div></font></div></code></div><br>The following cilent code is well-=
formed:</div><div><br></div><div><div class=3D"prettyprint" style=3D"border=
: 1px solid rgb(187, 187, 187); word-wrap: break-word; background-color: rg=
b(250, 250, 250);"><code class=3D"prettyprint"><div class=3D"subprettyprint=
"><div class=3D"subprettyprint">Demo x;</div><div class=3D"subprettyprint">=
for (int i =3D 0; i < 10; ++i) {</div><div class=3D"subprettyprint">=C2=
=A0 x.d[i] =3D i; /// Initialize x with 0~9</div><div class=3D"subprettypri=
nt">}</div><div class=3D"subprettyprint">proxy<Callable<void()>, T=
rivialWrapper<sizeof(Demo)>> p1(x), p2; /// p1 and p2 are proxy ty=
pes</div><div class=3D"subprettyprint">p2 =3D p1;</div><div class=3D"subpre=
ttyprint">p1(); /// Print 0~9</div><div class=3D"subprettyprint">p2(); /// =
Print 0~9</div></div></code></div><div><br></div>When assign p1 to p2, the =
memory block of p1 is directly copied to p2. Then, p1 and p2 holds the same=
value.</div><div><br></div><div>Everything a TrivialWrapper can solve can =
be solved with DeepWrapper. The differences between the two are the perform=
ance and scope of application:</div><div><ul><li><span style=3D"line-height=
: normal;">TrivialWrapper usually has higher runtime performance, because i=
t is not polymorphic, and<br></span></li><li><span style=3D"line-height: no=
rmal;">DeepWrapper usually has a wider scope of application, because it can=
be converted from a type of any size and=C2=A0regardless of whether the ty=
pe is trivial or not.</span></li></ul>I prefer to use TrivialWrapper in per=
formance sensitive cases, and DeepWrapper is recommended to be used in gene=
rality sensitive cases.</div><div><br></div><div>=C2=A0</div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px =
#ccc solid;padding-left: 1ex;">> =C2=A0 /* Meets the Wrapper requirement=
s */
<br>> =C2=A0 void* get() {
<br>> =C2=A0 =C2=A0 // The address of the wrapped object is calculated w=
ith a constant=20
<br>> offset
<br>> =C2=A0 =C2=A0 return data_.get();
<br>> =C2=A0 }
<br>>=20
<br>> =C2=A0private:
<br>> =C2=A0 MemoryBlock<SIZE> data_;
<br>> };
<br>>=20
<br>>=20
<br>> I am wondering if these design for wrappers (DeferredWrapper, Deep=
Wrapper,=20
<br>> SharedWrapper and TrivialWrapper) are adequate to be added to the =
standard=20
<br>> (discard of naming).
<br>
<br>I think you need to put in a lot of more work on why they are generally
<br>useful.
<br>
<br>/MF
<br></blockquote></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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/f50fbc34-4601-486b-8f56-b3cef5c6f2f3%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/f50fbc34-4601-486b-8f56-b3cef5c6f2f3=
%40isocpp.org</a>.<br />
------=_Part_708_1691306897.1498123488036--
------=_Part_707_1375949212.1498123488035--
.