Topic: RFC: Mixin, Inner, and Stateless Classes v0.5
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 29 Oct 2015 11:04:46 -0700 (PDT)
Raw View
------=_Part_10243_2020340158.1446141886863
Content-Type: multipart/alternative;
boundary="----=_Part_10244_1110093388.1446141886863"
------=_Part_10244_1110093388.1446141886863
Content-Type: text/plain; charset=UTF-8
A while back, we had the semi-annual "let's put properties in C++ discussion
<https://groups.google.com/a/isocpp.org/d/msg/std-proposals/n0QcD_K5dBY/3ZW3MpJrmhIJ>".
Unlike most such discussions however, that led to a more generalized idea: adding
with a restrictive form of Java-style inner classes
<https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/Yorc58iiBwAJ>,
which could be used to implement properties sanely. Several more concrete
ideas
<https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/G5MsctCECAAJ>
were put forth along these lines, including one of my own that introduced a
separate idea for stateless types
<https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/gxAxe5xBAQAJ>
..
However, that idea hit a large snag, in that inner classes have to be
nested in the classes they use. So if you're trying to implement something
complex like properties, you'd have to add tons of boilerplate code to make
the inner class proxy work. For every property you write. That's not to say
that inner classes are all about properties, but that is a use case of them.
The original inline class idea had the notion of being able to declare such
constructs outside of a class, but they had some limitations. Then, I
started playing around with the idea of allowing "inner classes" to be
declared outside of a type, but as a template. One of the template
parameters would be filled in with the actual class it is an inner class
of. So the common boilerplate could go into there.
And then, I realized that I was really just solving the CRTP problem. That
is, I was declaring a way to write a class that would be used by another
class, injecting its definitions into the other class as if they were one
type.
In short, I'd created language support for C++ mixins. Indeed, I realized
that inner classes and mixins are really kinda the same thing, if you think
about their effects.
So... here's a proposal that provides mixins, inner classes, and stateless
classes to C++. What do you think? Is this a functional idea? What cases
did I miss?
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_10244_1110093388.1446141886863
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">A while back, we had the semi-annual "<a href=3D"http=
s://groups.google.com/a/isocpp.org/d/msg/std-proposals/n0QcD_K5dBY/3ZW3MpJr=
mhIJ">let's put properties in C++ discussion</a>". Unlike most suc=
h discussions however, that led to a more generalized idea: <a href=3D"http=
s://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/Yorc58ii=
BwAJ">adding with a restrictive form of Java-style inner classes</a>, which=
could be used to implement properties sanely. Several <a href=3D"https://g=
roups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/G5MsctCECAAJ"=
>more concrete ideas</a> were put forth along these lines, including one of=
my own that <a href=3D"https://groups.google.com/a/isocpp.org/d/msg/std-pr=
oposals/u35GIuJECcQ/gxAxe5xBAQAJ">introduced a separate idea for stateless =
types</a>.<br><br>However, that idea hit a large snag, in that inner classe=
s have to be nested in the classes they use. So if you're trying to imp=
lement something complex like properties, you'd have to add tons of boi=
lerplate code to make the inner class proxy work. For every property you wr=
ite. That's not to say that inner classes are all about properties, but=
that is a use case of them.<br><br>The original inline class idea had the =
notion of being able to declare such constructs outside of a class, but the=
y had some limitations. Then, I started playing around with the idea of all=
owing "inner classes" to be declared outside of a type, but as a =
template. One of the template parameters would be filled in with the actual=
class it is an inner class of. So the common boilerplate could go into the=
re.<br><br>And then, I realized that I was really just solving the CRTP pro=
blem. That is, I was declaring a way to write a class that would be used by=
another class, injecting its definitions into the other class as if they w=
ere one type.<br><br>In short, I'd created language support for C++ mix=
ins. Indeed, I realized that inner classes and mixins are really kinda the =
same thing, if you think about their effects.<br><br>So... here's a pro=
posal that provides mixins, inner classes, and stateless classes to C++. Wh=
at do you think? Is this a functional idea? What cases did I miss?<br></div=
>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_10244_1110093388.1446141886863--
------=_Part_10243_2020340158.1446141886863
Content-Type: text/html; charset=UTF-8;
name="Mixins, Inner, and Stateless Classess.html"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="Mixins, Inner, and Stateless Classess.html"
X-Attachment-Id: 7c6bc3b0-d62f-406c-a9f0-a988f4a84d7b
Content-ID: <7c6bc3b0-d62f-406c-a9f0-a988f4a84d7b>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=3D"http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" =
/>
<meta http-equiv=3D"Content-Style-Type" content=3D"text/css" />
<meta name=3D"generator" content=3D"pandoc" />
<meta name=3D"date" content=3D"2015-10-29" />
<title>Mixin, Inner and Stateless Classes</title>
<style type=3D"text/css">code{white-space: pre;}</style>
</head>
<body>
<div id=3D"header">
<h1 class=3D"title">Mixin, Inner and Stateless Classes</h1>
<h3 class=3D"date">October 29, 2015</h3>
</div>
<div id=3D"TOC">
<ul>
<li><a href=3D"#concepts">Concepts</a><ul>
<li><a href=3D"#inverted-subobject">Inverted Subobject</a></li>
</ul></li>
<li><a href=3D"#current-implementations">Current Implementations</a><ul>
<li><a href=3D"#mixin">Mixin</a></li>
<li><a href=3D"#inner-class">Inner Class</a></li>
<li><a href=3D"#stateless-optimization">Stateless Optimization</a></li>
</ul></li>
<li><a href=3D"#uses">Uses</a><ul>
<li><a href=3D"#uses_properties">Properties</a></li>
<li><a href=3D"#proxies-and-interface-translation">Proxies and Interface Tr=
anslation</a></li>
</ul></li>
<li><a href=3D"#design">Design</a><ul>
<li><a href=3D"#stateless-classes">Stateless Classes</a></li>
<li><a href=3D"#inverted-class-members">Inverted Class Members</a></li>
<li><a href=3D"#inverted-class-scoping">Inverted Class Scoping</a></li>
<li><a href=3D"#inner-classes">Inner Classes</a></li>
<li><a href=3D"#mixin-classes">Mixin Classes</a></li>
</ul></li>
<li><a href=3D"#remaining-issues-to-resolve">Remaining Issues to Resolve</a=
></li>
</ul>
</div>
<p>This proposal specifies three new language features. Two of these featur=
es are really two halves of the same conceptual coin (though they are separ=
ate features). The third feature is an optimization for certain use cases t=
hat make the other two features much more attractive.</p>
<h1 id=3D"concepts">Concepts</h1>
<h2 id=3D"inverted-subobject">Inverted Subobject</h2>
<p>A subobject is itself an object, but it is wholly controlled by the obje=
ct in which it is contained. For the purposes of this discussion, we will c=
onsider two kinds of subobjects in C++: non-static data members (NSDMs) and=
base class members. An NSDM adds to the capabilities of the object to whic=
h it is a member by containment. Each NSDM is accessed independently, but i=
t is still fundamentally dependent on the object it is contained within. Ba=
se class subobjects are similar in that they expand the capabilities of the=
containing class. However, they expand those capabilities directly, bringi=
ng all of their functionality into the direct interface of the derived clas=
s (as appropriate to the derivation protection class, of course).</p>
<p>With subobjects of all kinds, control flows in one direction: from the c=
ontaining object to the objects it contains. Base classes can access their =
own members as well as any members of one of their base classes. But no bas=
e class can (directly) access members of its derived class. Similarly, NSDM=
s can access any NSDMs of their own, but they cannot access the interface o=
f the object that contains them.</p>
<p>What this proposal suggests doing is changing this relationship. An &quo=
t;inverted subobject" is one where the relationship between the contai=
ner and the contained objects are inverted. Inverted base subobjects can ac=
cess their (direct) owning object, and inverted NSDM subobjects can access =
the object that they are contained within.</p>
<p>It turns out that both of these kinds of control inversions are in use t=
oday, either in C++ itself or in other languages. While conceptually these =
are just variations of the same thing, these features are often implemented=
as two distinct features with two distinct names.</p>
<p>Java provides the name for an NSDM that can access its owning class: inn=
er class. The other concept's name is defined by the effects of the concept=
.. If a base class can access the derived class's members, then it can add f=
unctionality to that class which integrates seamlessly with any particular =
implementation that provides the appropriate interface. Let us refer to thi=
s as a mixin.</p>
<p>Note that mixins effectively allow a form of static polymorphism. The no=
rmal way to invert relationships in a hierarchy is to use <code>virtual</co=
de> functions. The derived class implements the virtual interface declared =
by the base class. But this can be inefficient, due to the overhead of virt=
ual calls.</p>
<p>Mixins effectively allow the same inversion in a static way. Not unsurpr=
isingly, mixins in C++ are usually implemented using the static polymorphis=
m tool of C++: templates.</p>
<h1 id=3D"current-implementations">Current Implementations</h1>
<p>Inner classes and mixins can be implemented in the language as it curren=
tly stands. The question is what kind of gymnastics are required to impleme=
nt them, as well as the ability to compose them from different library impl=
ementations. And of course how easy it is to get the implementation wrong.<=
/p>
<h2 id=3D"mixin">Mixin</h2>
<p>For our purposes, mixin implementations should allow:</p>
<ol style=3D"list-style-type: decimal">
<li><p>The mixin class to access the members of the class it "mixes&qu=
ot; into. How much access is a question, but even just public access would =
be functional.</p></li>
<li><p>The class being mixed into can access the members of the mixin. Agai=
n, just public members would be something.</p></li>
<li><p>Users of the class being mixed into should be able to access public =
members of either class through an instance of the containing class.</p></l=
i>
<li><p>Multiple mixins can be applied to the same class (to the degree that=
their interfaces do not conflict). And separate mixins into the same type =
can effectively communicate with one another.</p></li>
<li><p>Mixin composition. That is, building a new mixin which is the sum of=
itself with a second mixin.</p></li>
</ol>
<p>The rules of C++ do not allow a concrete class to be able to call functi=
ons it does not know about. And since we want mixins to be arbitrarily comp=
ose-able, a mixin cannot know what it is derived from. The obvious solution=
here is a template of some kind.</p>
<p>There are two general strategies for building mixins in C++. One relies =
on explicitly inverting the class hierarchy, while the other involves using=
a regular hierarchy and clever casting tricks. They each have their drawba=
cks.</p>
<h3 id=3D"hierarchy-inversion">Hierarchy Inversion</h3>
<p>In this case, mixins are implemented by having the mixin be the <em>deri=
ved</em> class. Each such mixin looks something like this:</p>
<pre><code>template<typename Base>
struct mixin : public Base
{
//Add members here.
};</code></pre>
<p>Users in this case do not use the <code>Base</code> class directly. They=
use the fully composed mixin type, which is built from a nested template s=
equence, usually hidden in an alias:</p>
<pre><code>using MyClass =3D mixin1<mixin2<mixin3<Base>>>=
;</code></pre>
<p>This allows mixins to have access to <code>Base</code> through a templat=
e interface. Concepts can be used to specify the particular interface neede=
d.</p>
<p>The downsides of this approach are many. It does not fulfill #2, as the =
base class cannot directly communicate with any mixins that it uses. This i=
s an acceptable limitation if the class's main functionality is built from =
mixins. But if a mixin is being used to extend the class's functionality, t=
his hierarchy inversion becomes untenable.</p>
<p>It also makes it impossible to have constructs like private mixins, wher=
e the mixin provides some facility that is merely used by the <code>Base</c=
ode> class without being exposed to external code. Yet this it still needs =
to be a mixin, since that functionality requires the inverted relationship.=
So these mixins are only useful for public interfaces.</p>
<p>It also makes #3 difficult, as users must name the entire mixin chain wh=
en naming the type. Obviously a typedef makes this work just fine, but it d=
oes make the ultimate typename rather cumbersome. It also means that <code>=
mixin1<mixin2<Base>></code> is a fundamentally different type f=
rom <code>mixin2<mixin1<Base>></code>, despite being functional=
ly identical.</p>
<p>On the plus side, it makes it easy for two mixins to communicate effecti=
vely. Granted, the communication can only proceed in one direction. But mix=
in composition works.</p>
<p>Another positive of this approach is that it's very simple and difficult=
to get wrong. It relies on normal inheritance relationships in C++. Invers=
ion is achieved simply by deriving in the opposite direction.</p>
<h3 id=3D"crtp">CRTP</h3>
<p>The curiously reoccurring template pattern (CRTP) can be used for a more=
featureful mixin. The way this works is simple. The mixin is a template, w=
hich is passed the <em>derived</em> class as a template argument. Thus, the=
derived class inherits the interface of the mixin. But because the mixin k=
nows its derived class type, it is able to perform a <code>static_cast</cod=
e> of its <code>this</code> pointer to that type. And thereby access the de=
rived class's members.</p>
<p>Here is an example implementation of this:</p>
<pre><code>template<typename derived>
class mixin
{
public:
void caller() const
{
auto _this =3D get_this();
_this->some_function();
}
private:
derived *get_this() {return static_cast<const derived *>(this);}
const derived *get_this() const { return static_cast<const derived *=
>(this); }
};
class destination : public mixin<destination>
{
public:
void some_function() {...}
};</code></pre>
<p>This kind of mixin satisfies all of the requirements. There is full cros=
s-talk; the derived class can access the mixin and vice-versa. Though they =
can only access the public members of each, that is an acceptable limitatio=
n. Users of the derived class can access the mixin's public members (assumi=
ng public inheritance) as though they were members of the derived class. An=
d thanks to multiple inheritance, it is easy for one type to use multiple m=
ixins. Indeed, mixins can be composed with one another.</p>
<p>The most obvious problem is the inconvenience. The need to convert the <=
code>this</code> pointer manually is a particular annoyance. But we don't n=
ecessarily add language features for simple convenience. So instead, let us=
look at where this pattern breaks down.</p>
<p>First, it is difficult to compose mixins through inheritance. That is, i=
f we have one mixin <code>A</code>, and create a second mixin <code>B</code=
> which is derived from <code>A</code>, it can create a problem for <code>B=
</code>. Consider this:</p>
<pre><code>template<typename derived>
class A {...};
template<typename derived>
class B : public A<B<derived>> {...};</code></pre>
<p>If <code>A</code> has some interface that it expects from <code>derived<=
/code>, then by the way this interface works, <code>B</code> <em>must</em> =
expose that interface. However, since <code>B</code> is itself a mixin, the=
idea should be that <code>B</code> <em>inherits</em> the interface of <cod=
e>A</code>. That is, whatever interface <code>A</code> requires becomes par=
t of <code>B</code>'s interface requirements too. <code>B</code> of course =
has the opportunity to implement that interface, but it should not <em>have=
</em> to.</p>
<p>Above, mixins were analogized to dynamic polymorphism, and that helps de=
monstrate the limitation with the mixin version. If <code>A</code> had pure=
-virtual member functions, <code>B</code> would not necessarily have to imp=
lement them. If it does not, then <code>B</code> becomes an abstract class,=
and users of <code>B</code> must implement whatever pure-virtual members t=
hat <code>B</code> does not implement.</p>
<p>If mixins are to be the static equivalent, then they should provide the =
same functionality. Yet the CRTP version of mixins doesn't allow this. <cod=
e>B</code> must provide the entire interface. It can forward those calls to=
its own <code>derived</code>, but it must do this explicitly for every fun=
ction (or variable) that <code>A</code> requires.</p>
<p>The CRTP implementation has no way to check for mistakes. Here are some =
things you have to do with such mixins that are not (directly) checked for:=
</p>
<ul>
<li>Use the correct type when deriving from them. The compiler will catch t=
he error in the implementation, but only because it's instantiating the tem=
plate and can't find the interface it expects. So the compilation error wil=
l be far from the actual problem.</li>
<li>Not use virtual inheritance. That breaks the <code>static_cast</code>. =
The compiler can't check for that one.</li>
<li>Never declare objects of mixin types directly. That is, you cannot writ=
e this declaration: <code>mixin<SomeType> a;</code>. The compiler won=
't catch that either; the <code>static_cast</code> will assume that the mix=
in is derived from <code>SomeType</code>. And if it isn't, then you'll get =
odd runtime failures.</li>
</ul>
<p>So the CRTP implementation is somewhat fragile.</p>
<p>One final problem has to do with the size of the resulting object. Many =
mixins have no NSDMs. One of the main purposes of mixins is to allow the co=
ntained object to provide the storage for some property, or to otherwise si=
mply modify the interface of the contained type.</p>
<p>Even so, such objects can impact the size of the resulting object. If th=
e contained type was standard layout, then empty base optimization takes ca=
re of the issue. But if it was not (perhaps the containing object has virtu=
al functions, or some other mixin brings in its own members), then compiler=
s are free to not optimize empty bases. And therefore, you cannot rely on s=
uch optimizations. If class size is of importance to you, every mixin poten=
tially could be making your class bigger.</p>
<p>Needlessly.</p>
<h2 id=3D"inner-class">Inner Class</h2>
<p>The concept of Java-style inner classes needs some explanation, as it is=
very unlike normal C++ classes.</p>
<p>In C++, if you declare a class within another class definition (nested c=
lasses), the nested class has absolutely no relationship with the class tha=
t it is declared within. It is not even a friend of it, let alone a part of=
it. In C++, nested classes exist solely for scoping of the class's name.</=
p>
<p>In Java, a nested class is implicitly the friend of the class they nest.=
This allows nested Java classes to act as part of the class's interface. B=
ut a non-static nested class is even more special; it is an inner class.</p=
>
<p>Java inner classes implicitly allow their own <code>this</code> pointer =
to convert into a pointer to the instance that their current object instanc=
e is a member of. And since they are implicitly friends, they have direct a=
ccess to all members of their containing instance. So if there is an instan=
ce <code>a</code> of a class <code>A</code>, and it has an inner class memb=
er <code>b</code>, then any non-static member function of <code>b</code> co=
uld call non-static member functions in <code>a</code>. Not just in the cla=
ss, but in the <em>specific instance</em> <code>a</code> which holds this p=
articular <code>b</code> instance object.</p>
<p>One can pass Java inner class members around to other functions, which c=
an call functions on those inner class members. The members that get called=
can themselves still access <code>a</code>, even though the function that =
called them was passed <code>b</code>.</p>
<p>In terms of implementation, what is needed is a way to transform <code>b=
</code>'s <code>this</code> pointer into a pointer to its owning object of =
type <code>A</code>. In Java, this is done by literally sticking a hidden p=
ointer into the inner class type; garbage collection handles lifetime issue=
s. This also allows Java inner classes to be declared outside of the class =
they are nested within. The code allocating it still has to have an object =
of that type, but the variable is not directly a part of the owning class.<=
/p>
<p>Without generalized garbage collection, C++ can't really do that. As suc=
h, implementing inner classes in C++ is difficult. But this is not impossib=
le. It does require quite a bit of help, though.</p>
<p>The general idea is to first restrict inner classes to being NSDMs of th=
eir containing instances. Then, have the containing class's constructors (a=
ll of them, so kiss trivial copyability goodbye) pass a <code>this</code> t=
o every inner class member as part of those members' construction. Each inn=
er class member stores a copy of that pointer. When an inner class member i=
s being copied, care must be taken not to copy the <code>this</code> member=
(so the inner class itself can't be trivially copied either).</p>
<p>These kinds of implementations are functional. But unlike mixins, it's a=
lot harder to ignore the difficulties of implementation. <em>Every</em> co=
nstructor of the owning class has a bit of boilerplate in it, making the wh=
ole process quite fragile. Every constructor of the inner class has to stor=
e the <code>this</code> pointer correctly.</p>
<p>Also, the implementation causes problems for the owning type. Because of=
the boilerplate code, the owning type cannot be a trivially copyable class=
..</p>
<p>Composition of inner classes (inner classes of inner classes) is problem=
atic, for the same reason as the CRTP-based mixin implementation. Each inne=
r class gets to access its direct outer class, but no outer classes beyond =
that without even more special coding. Though at least in this case, becaus=
e inner classes are concrete classes, it is at least possible for the inner=
class to read the outer inner class's pointer to <em>its</em> outer class.=
But that requires that the user manually determine which function in which=
class in the inverted hierarchy needs to be called and then use the correc=
t pointer to make that call.</p>
<p>Another problem is that you cannot make inner classes who's definitions =
come from elsewhere. So you cannot make a library of inner classes that oth=
ers use inside their classes.</p>
<p>Oh, and while empty base optimization is sometimes possible for mixins, =
there is no allowance in C++ for empty <em>member</em> optimizations. So ev=
ery empty inner class member bloats the class. Though the fact that "e=
mpty" inner classes still need that <code>this</code> pointer means th=
at they're not empty in <em>practice</em>, just in concept.</p>
<h2 id=3D"stateless-optimization">Stateless Optimization</h2>
<p>There are many uses of these inverted subobjects. However, quite a few o=
f them involve the inverted class having no NSDMs at all. The state these o=
bjects manipulate or access is provided by the owning class, with the mixin=
/inner class merely providing an interface.</p>
<p>The problem is that, by the rules of C++, all members do take up space i=
n the final class. While empty mixins are required to take up no space when=
used as bases of standard layout types, there is no such optimization for =
non-static data members. And even with base classes, if the owning class ca=
nnot be standard layout, then removing this space is entirely optional.</p>
<p>This issue has been one of the main reasons why people do not find some =
library implementations of properties attractive (implemented as a very lim=
ited form of inner classes). Making the interface slightly nicer to use is =
usually not worth the overhead. Therefore, finding a way to remove this ove=
rhead would be ideal, and would for many people be considered a necessary f=
irst step.</p>
<p>As such, this proposal includes not only the two inverted subobject type=
s, but a third feature that both of the other two can make significant use =
of. This is the ability to declare that an empty class shall not take up ro=
om when declared as a subobject of another type.</p>
<h1 id=3D"uses">Uses</h1>
<p>The uses of full mixins are legion. And the ability to make them statele=
ss where appropriate is the icing on the cake, as it allows the containing =
class to provide data storage for the mixin's interface. For example <code>=
enable_shared_from_this</code> effectively stores a <code>weak_ptr</code> (=
or the guts of one) in the class. If it were a true mixin, it could allow t=
he class implementation to store the <code>weak_ptr</code> in a data struct=
ure of its choosing. This would also allow the class to access the <code>we=
ak_ptr</code> directly, whereas currently you have to do <code>weak_ptr<=
T>(shared_from_this())</code>, which involves a needless quantity of ato=
mic operations.</p>
<p>And all of this comes without virtual function overhead.</p>
<p>Stateless classes which are not inverted have limited utility, but they =
still retain some. For example, iterator categories are tested currently vi=
a the presence of certain base class tags. These tags have no state besides=
the fact that they exist. These and similar types are reasonable use cases=
for statelessness without inverted classes.</p>
<p>It is inner classes where their utility requires some justification, as =
they are rarely seen in C++.</p>
<h2 id=3D"uses_properties">Properties</h2>
<p>Full disclosure: I am personally not a big fan of properties. However, I=
also know that this is a thing people want, and the thread/proposals that =
led to me developing this one were primarily about getting properties into =
the language somehow. Obviously, this feature has evolved since then.</p>
<p>Stateless inner classes make it possible for users to implement properti=
es with zero overhead. Prior property implementations also seemed really ha=
ckey; they would require things like lambdas as initializers to some struct=
, or the property itself would have to hold the value (thus making it impos=
sible for properties to represent things that were not values). With statel=
ess inner classes, the property implementation has full access to most of t=
he powers of the C++ object model, without a bunch of boilerplate to access=
the data.</p>
<p>They can overload <code>operator=3D</code> to perform assignment as the =
"setter" operation. They can overload <code>operator Typename</co=
de> for implicit conversions as the "getter" operation. If the un=
derlying value is of an appropriate type, they can even overload math assig=
nment operators like <code>operator+=3D</code> and such. In effect, the pro=
perty object can be a full proxy for the real thing, providing access, modi=
fication, or both, in all of the ways that C++ allows. And all without taki=
ng up added space.</p>
<p>Because there is a lot of boilerplate here to make a good property that =
plays by C++'s rules, this boilerplate can almost certainly be abstracted o=
ut... into a stateless <em>mixin</em>. The inner class property itself woul=
d be responsible only for providing a <code>get</code> function and a <code=
>set</code> function. The mixin is what would do all of the work for operat=
or overloading and such.</p>
<p>Template polymorphism can be used here as well to improve the interface.=
For example, if the return value of <code>get</code> is a pointer (or a ty=
pe that implements <code>operator-></code>), the property can synthesize=
an appropriate <code>operator-></code> overload. If no <code>set</code>=
method is provided, then the property does not synthesize assignment opera=
tors. If no <code>get</code> method is provided, the mixin does not allow d=
ata access. But more complex rigging can be accomplished as well.</p>
<p>If there is a non-const <code>get</code> overload that returns a referen=
ce, and there is no <code>set</code> method, then the mixin assumes that <c=
ode>get() =3D expr</code> is an acceptable means of changing the value. So =
its overloaded assignment operators will be synthesized by calling thenon-c=
onst <code>get</code>. Furthermore, a non-const <code>get</code> allows the=
system to synthesize the <code>operator+=3D</code> and similar operators w=
ithout having a <code>get</code>/<code>set</code> round-trip.</p>
<p>And thus, a property definition in a class would look like this:</p>
<pre><code>stateless inner struct : property<mixin> //Anonymous struc=
t definition.
{
float &get() {return value;} //non-const reference means `proper=
ty` will use this to set as well.
const float &get() const {return value;}
} property_name;</code></pre>
<p>The mixin does all of the complex work that synthesizes the appropriate =
operator overloads. The prefix part could even be wrapped up in a macro.</p=
>
<h2 id=3D"proxies-and-interface-translation">Proxies and Interface Translat=
ion</h2>
<p>Properties are really just a specific instance of a more general use cas=
e for stateless inner classes: interface translation without creating new p=
roxy objects.</p>
<p>Let's say that you have some container class <code>C</code>. It has an i=
nterface that works for you and your needs. But with the ranges proposal, y=
ou know that it doesn't really fit into that paradigm. It is conceptually a=
range, but it doesn't actually fit the range paradigm. And you don't want =
to rewrite your interface; if it's not broken, don't fix it.</p>
<p>The way to resolve that today would be to create some proxy object <code=
>PC</code>, which internally stores a pointer/reference to a <code>C</code>=
instance. This object would translate the interface of <code>C</code> into=
one appropriate for the ranges proposal.</p>
<p>A more pressing concern is one of lifetime. Because <code>PC</code> is a=
n object type, which the user can declare values of, it is now possible to =
take a <code>PC</code> and possibly store it long term. And since it stores=
a pointer/reference to <code>C</code>, that makes it way too easy for some=
one to think that they have a real object rather than a proxy.</p>
<p>If the proxy object is an inner class instance however, the proxy <em>ca=
nnot</em> be stored. A reference to it can be stored, but then it is abunda=
ntly clear that you're storing a <em>reference</em>, not a container-like o=
bject. And thus, it's clear to everyone that you have stored a reference to=
some other object, which is what virtually every proxy object is.</p>
<p>It also alleviates the small overhead to using proxies. A regular proxy =
object has to be constructed every time a proxy is created. Whereas an inne=
r class proxy is a fixed part of the class, rather than generated as needed=
by the user. It can be stateless, with all of the data it's accessing stor=
ed within its owner. This saving an indirection, since getting the pointer =
to its owner is a static operation rather than a memory access.</p>
<p>Proxy iterators are a good place to see where this overhead could be non=
-trivial. Particularly in proxy output iterators, every time you do <code>*=
it++ =3D ...</code>, you call the proxy's constructor, which copies a point=
er/reference to whatever is needed into the proxy object. It gets used once=
to do the copy, then destroyed.</p>
<p>With inner classes, the proxy type can be a stateless inner class of the=
iterator type itself. So <code>operator*</code> returns a reference to the=
inner class. The inner class's <code>operator=3D</code> will store the val=
ue, possibly updating the position of the iterator. And this neatly avoids =
having to create and destroy an object every time insertion happens.</p>
<p>Aggressive compiler optimization may alleviate some of these concerns, b=
ut inner classes ensure that they don't exist.</p>
<p>This kind of translation also makes it possible to provide different way=
s to view the same data. An example used to illustrate this was a class tha=
t stores colors. The class would internally store the color in the sRGB col=
orspace, but through a stateless inner class interface, you could modify th=
e value in the HSV colorspace.</p>
<p>Without inner classes, this would require a bunch of explicit calls to c=
onversion functions: <code>color.set_hsv(color.get_hsv().set_hue(new_hue))<=
/code>. With stateless inner classes, it requires <code>color.hsv.hue =3D n=
ew_hue</code>. This is much cleaner and clearer to the user. And probably m=
ore efficient.</p>
<p>One could even do <code>color.hsv.hue +=3D new_hue</code>. Without the i=
nner class, a similar operation would have either required an explicit <cod=
e>add_hue</code> function or the user would need to do this:</p>
<pre><code>auto &&hsv =3D color.get_hsv(); //Avoid double get call.
color.set_hsv(hsv.set_hue(hsv.get_hue() + new_hue));</code></pre>
<p>This takes up two lines now. If you accept the double get, you get the g=
argantuan expression:</p>
<pre><code>color.set_hsv(color.get_hsv().set_hue(color.get_hsv.get_hue() + =
new_hue)</code></pre>
<p>And remember: all of these inner classes can be <em>stateless</em>. All =
they do is enhance the class's interface; they impose no overhead on their =
containing classes.</p>
<p>Not every set of types with different representations should use such vi=
ews. For example, <a href=3D"https://github.com/HowardHinnant/date">Howard =
Hinnant's Date/Time library</a> provides <code>day_point</code> and <code>y=
ear_month_day</code>, which are two versions of the same data. I would not =
suggest giving <code>day_point</code> a view that provides <code>year_month=
_day</code>'s interface; they should be two separate types, due to the conv=
ersion overhead.</p>
<p>Though it could still have property-like objects for setting/getting dat=
es in different formats.</p>
<h1 id=3D"design">Design</h1>
<p>Now that we have discussed what these features are, their limitations wi=
th current implementations, and what we could gain from putting them in the=
language, let's talk about how to add them into the C++ language. Here are=
the goals for the overall design of these features:</p>
<ul>
<li><p>Inverted and stateless classes should have as few restrictions on th=
eir use and nature as is implementable. At least, those restrictions that a=
re not based on their design. For example, inner class instances are direct=
ly associated with an instance of their containing class. As such, it makes=
no sense to be able to declare automatic variables of them or put them on =
the heap, so restricting them to being declared as members of that class is=
acceptable. However, it should be perfectly reasonable to pass around refe=
rences or pointers to such objects.</p></li>
<li><p>If a class contains inverted subobjects, the class should be impacte=
d by this as little as possible, except where obvious. In particular, we sh=
ould try as much as possible to avoid complicating standard layout and triv=
ial copy-ability rules, unless it is absolutely necessary to the implementa=
tion.</p></li>
</ul>
<p>A word about syntax. C++ makes adding new keywords difficult. However, P=
0056 suggests a way to make adding keywords rather painless. Therefore, thi=
s proposal will be written assuming that this has been adopted. So it will =
introduce a number of soft keywords. Consider what is proposed here to be t=
he "perfect" syntax. Obviously in the absence of soft keywords, w=
e would have to implement this a different way.</p>
<p>In short: don't fret over the syntax for the time being. We can iron tha=
t out later.</p>
<p>Oh, and many of the elements of the design are up for debate (like the r=
each of the various hierarchy inversions through other code). This document=
should be looked on as a start point and a rational, not the final word.</=
p>
<h2 id=3D"stateless-classes">Stateless Classes</h2>
<p>A stateless class is a class that is declared with the following syntax:=
</p>
<pre><code>std::stateless class ClassName : BaseClasses
{
...
};</code></pre>
<p>The <code>std::stateless</code> soft keyword must be associated with any=
forward declarations for stateless types.</p>
<pre><code>class ClassName; //Not a stateless type.
std::stateless class ClassName //Compiler error: you told me it wasn't=
stateless before.
{
};</code></pre>
<p>And vice-versa.</p>
<p>A stateless class may not:</p>
<ul>
<li>Have any NSDMs that are not themselves stateless classes.</li>
<li>Have any base classes that are not themselves stateless classes.</li>
<li>Have any virtual member functions.</li>
<li>Have any virtual base classes.</li>
</ul>
<p>The size of a stateless type is not modified by being stateless. That is=
, you will get what you would normally expect from an ordinary empty class.=
</p>
<p>The standard library should have an appropriate template metamethod (and=
variable) for testing if a type is stateless.</p>
<p>Stateless classes only have a special effect when declared as subobjects=
of another class type. When a stateless class is an NSDM subobject, it has=
no effect on the size or layout of that type. A stateless NSDM does not al=
ter the layout of the class of which it is a member. A stateless non-virtua=
l base class does not alter the storage or layout of the derived class. Thu=
s, the layout/size of a type is only affected by its non-stateless/virtual =
subobjects.</p>
<p>Stateless object members otherwise have the same effects on their contai=
ning types as regular types do. For example, a stateless class can cause it=
s owners to not be trivially copyable by itself having a non-trivial copy c=
onstructor (even though the type by definition has nothing to copy).</p>
<p>Template types may be stateless as well. However, individual specializat=
ions are allowed to declare themselves to change their stateless status. So=
you could declare that a <code>tuple</code> would be stateless iff all of =
its members are stateless.</p>
<p>In all other ways (except where discussed below), stateless types can be=
used exactly like regular types.</p>
<h3 id=3D"implementation-and-restrictions">Implementation and Restrictions<=
/h3>
<p>In a perfect world, the above would be the only restrictions on stateles=
s types. C++ of course is never perfect.</p>
<p>In C++ as it currently stands, every object needs to have a memory locat=
ion. And two unrelated types cannot have the <em>same</em> memory location.=
</p>
<p>Stateless types effectively have to be able to break this rule. The memo=
ry location of a stateless member subobject can be the same location as the=
object it is a member of. And two sibling members of the same type may hav=
e the same location if one of them is stateless.</p>
<p>This is not really an implementation problem, since stateless classes by=
their very nature have no state to access. As such, the specific nature of=
their <code>this</code> pointer is irrelevant. So we simply need a strict =
aliasing rule that effectively allows stateless types to be able to have th=
e same memory location as any other type (whether stateless or not). This s=
hould not cause much implementation headache, beyond having to change the l=
ayout rules to account for objects with no size.</p>
<p>A much bigger problem happens due to arrays. Or more specifically, point=
er arithmetic on arrays.</p>
<p>In C++, the following should be perfectly valid for any type:</p>
<pre><code>T t[5];
T *first =3D &t[0];
T *last =3D first + 5;
assert(sizeof(T) * 5 =3D=3D last - first);</code></pre>
<p>The whole point of a stateless type is that it does not take up space wi=
thin another type. If the array above were a member of another type, this c=
ode should still work. But, since <code>sizeof(T)</code> is non-zero, that =
means that the member <code>t</code> takes up room in its containing type.<=
/p>
<p>There are several ways to deal with this:</p>
<ol style=3D"list-style-type: decimal">
<li><p>Declare that <code>sizeof</code> for stateless types is zero. I am n=
ot nearly familiar enough with the C++ standard to know the level of horror=
s that this would unleash, but I can imagine that it's very bad.</p></li>
<li><p>Declare that stateless types will only not take up space if they are=
not declared as arrays.</p></li>
<li><p>Forbid declaring arrays of stateless types altogether.</p></li>
</ol>
<p>While #3 may seem a bit harsh, I think it is the best choice. For one, s=
tateless types have no state, so declaring an array of them is pointless.</=
p>
<p>The downside of #3 is that it may impact template code, where the templa=
te does not know (or care) that a type is stateless. This would also be the=
first C++ construct that actively forbids such declarations on complete ty=
pes. It would also forbid uses of stateless arrays in other contexts (stack=
variables, heap allocations), though primarily for orthogonality.</p>
<p>If the lack of orthogonality is too great, #2 can be adopted instead. Th=
e downside of #2 is that the statelessness of the class appears conditional=
: they don't take up room in other objects, except when they are arrayed. T=
his also means that a stateless class must forbid NSDMs which are arrays of=
stateless classes.</p>
<p>Oh, and while the above definition only requires stateless classes to no=
t take up space when used as subobjects, compilers are allowed to optimize =
their space usage in other cases too. While heap allocations must take up r=
oom, other allocations do not. So if a user declares a stateless member on =
the stack or as a global/namespace/member-static, the compiler is permitted=
to give the object no actual space.</p>
<h2 id=3D"inverted-class-members">Inverted Class Members</h2>
<p>Inverted classes of either kind can access their owning instance members=
from their member functions. But not from within <em>all</em> of their mem=
ber functions.</p>
<p>Inverted class constructors and destructors explicitly do not have acces=
s to their owning class members. This is for the same reasons why virtual f=
unctions don't work in them. Member subobjects are constructed before their=
containing objects, so their owners don't exist yet. And member subobjects=
are destroyed after the destruction of their containing objects, so they w=
ould be accessing objects that have stopped existed.</p>
<p>But in all other cases, inverted class non-static member functions can a=
ccess their owning class members. What access level they get is a question =
to be determined.</p>
<h2 id=3D"inverted-class-scoping">Inverted Class Scoping</h2>
<p>The current design allows inner classes and mixins to access <em>everyth=
ing</em> from their containers, with such access propagating through inner =
classes and mixins up to the first non-inner class/mixin type. This include=
s private members.</p>
<p>This seems like it breaks encapsulation a lot. However, I would argue th=
at it does not truly do so.</p>
<p>Inner classes can only be declared within another class declaration. The=
y are therefore as much a part of that class as one of its member functions=
.. Thus, they have every right to access the privates of their containing cl=
ass.</p>
<p>Mixins are a bit more troublesome in theory. A mixin is not contained wi=
thin a class definition. So giving it private access makes it seem like use=
rs can gain access to a type whenever they want.</p>
<p>However, the mixin syntax proposed below requires that the mixin is adde=
d to the class at declaration time. And therefore, the implementer of the c=
lass is the one who decides which mixins to use and which not to use. So us=
ers are unable to inject a mixin into a class that does not deliberately ch=
oose to use it. Using a mixin is thus like declaring that a class is a frie=
nd: the implementer is still the one in control of who gets access.</p>
<p>If this is considered a deal-breaker, we can restrict mixins to only bei=
ng able to access the public members of those they are mixed into. Alternat=
ively, we can add syntax to specify what a particular mixin has access to. =
The class deriving from the mixin would determine this.</p>
<h2 id=3D"inner-classes">Inner Classes</h2>
<p>An inner class is a nested class that is declared as follows:</p>
<pre><code>std::inner class Name
{
...
};</code></pre>
<p>Inner classes may only be declared as nested classes (they may be nested=
within another inner class). The class an inner class is declared within i=
s the direct owning class of that inner class.</p>
<p>As with normal class definitions, <code>Name</code> is optional (anonymo=
us inner classes can be are quite useful). The <code>inner</code> keyword i=
s part of the class's declaration, so if it is forward declared, it must be=
consistently declared with it.</p>
<p>An inner class may or may not have the <code>stateless</code> keyword ap=
plied to it. If it does, they can go in either order.</p>
<p>An inner class can be derived from non-inner classes normally. An inner =
class may be derived from another inner class, but only if the <em>direct</=
em> owning class of both is the same, or the direct owning class of the der=
ived inner class it itself derived (non-virtually) from the direct owning c=
lass of the base inner class.</p>
<p>Or to put it another way, from the derived inner class, it should be pos=
sible to go to its direct owner, then walk down the class hierarchy (non-vi=
rtually) to the class that is the direct owning class of the base inner cla=
ss.</p>
<p>A non-inner class may not be derived from an inner class type.</p>
<p>Inner classes may have members and other definitions as befitting a regu=
lar class.</p>
<p>The standard library should have a template metafunction (and variable) =
for testing if a type is an inner class type.</p>
<h3 id=3D"type-usage">Type Usage</h3>
<p>Variables of inner class types can only be declared as NSDM's of a class=
.. And the class which they are declared within must be either:</p>
<ul>
<li>The direct owning class of the inner class.</li>
<li><p>A class non-virtually inherited from the direct owning class of the =
inner class. The derived class in question should have access to the type (=
so private/protected inheritance can sever access, even without virtual inh=
eritance).<a href=3D"#fn1" class=3D"footnoteRef" id=3D"fnref1"><sup>1</sup>=
</a></p>
<p>Note that this means that the class which contains an instance of an inn=
er class need not be exactly its owner. However, the container does need to=
be non-virtually derived from the owner.</p></li>
</ul>
<p>Inner class types cannot be used as the type in <code>new</code> express=
ions. Objects of inner class types cannot have their destructors explicitly=
called. Temporaries cannot be created of inner class types.</p>
<p>Pointers and references to inner class types work as normal for any type=
..</p>
<p>Inner classes cannot be aggregated into arrays. The reason for this rest=
riction <a href=3D"#inner_impl">is discussed later</a>, but it is primarily=
due to implementation limitations.</p>
<p>How inner classes affect layout compatibility, trivial copyability, and =
various other aspects is very important. These decisions also factor heavil=
y into how compilers can implement them. This section will list the decisio=
n that we propose to use; how it is possible to implement this will be <a h=
ref=3D"#inner_impl">discussed later</a>. The specific interactions with the=
containing class are:</p>
<ul>
<li>Inner class members can be copied/moved exactly as any other type. Spec=
ifically, if a stateful inner class type is trivially copyable, it should b=
e legal to do a <code>memcpy(dest, src, sizeof(T))</code> from it to anothe=
r instance of that type, even if the destination instance is in a different=
containing object instance.</li>
<li>The fact that a particular NSDM is of an inner class type has no effect=
on <a href=3D"http://en.cppreference.com/w/cpp/concept/TriviallyCopyable">=
trivial copyability</a> with regard to the containing class. So an inner cl=
ass member could only prevent the containing class from being trivially cop=
yable if the inner class itself were not trivially copyable.</li>
<li>Types that declare stateful inner class members cannot have a trivial d=
efault constructor, and therefore cannot be <a href=3D"http://en.cppreferen=
ce.com/w/cpp/concept/TrivialType">trivial types</a>.</li>
<li>Stateless inner class members do not affect the layout of the class con=
taining them.</li>
<li>Stateful inner class members prevent their owning types from being stan=
dard layout.</li>
</ul>
<p>In all other ways, inner class types work just like regular types.</p>
<h3 id=3D"template-specialization">Template Specialization</h3>
<p>If an inner class is a template, then all of the specializations of that=
template must also be inner classes. And vice-versa. A template class's in=
stantiation will therefore be either inner or not inner, regardless of the =
template parameters used on it.</p>
<p>The purpose of this rule is really user-sanity. Users probably don't mea=
n to declare a specialization of some inner class template to not be an inn=
er class. And vice-versa. Also, since the domain of inner class definitions=
is limited to within some other class, the potential for someone injecting=
such a definition is low. So it's far more likely that this would happen d=
ue to user error than deliberate action.</p>
<h3 id=3D"containment-scope">Containment Scope</h3>
<p>Inner classes are implicitly friends of their owning class. If the ownin=
g class is itself an inner class, this friendship propagates up the ownersh=
ip hierarchy to the first non-inner class type.</p>
<p>Within member functions of inner classes (except for constructors and de=
structors), the following rules are added.</p>
<p>Members (static or not) of an inner class may access members of its owni=
ng instance through a pointer/reference to its own class (including implici=
t <code>this</code>) as though it were derived from its owning class type. =
Name lookup takes place as though the owning class were placed at the end o=
f the inner class's list of derived classes. Therefore explicitly derived c=
lasses take priority over owning ones. This implied derivation propagates u=
p the ownership hierarchy through inner classes until the first non-inner c=
lass is reached.</p>
<p>Qualified lookup within inner class member functions can also be perform=
ed up the ownership hierarchy as well. Similarly, inner classes may implici=
tly convert pointers/references to their own types into pointers/references=
to their owners, up the hierarchy until the first non-inner class is reach=
ed.</p>
<p>Note that the above properties are also conferred onto any lambda functi=
ons created by such members. Friends of inner classes may gain these abilit=
ies as well (mainly to allow lambdas to work). Non-friends of inner classes=
cannot do these.</p>
<p>Non-friends may perform explicit conversions from a pointer/reference to=
an inner class to a pointer/reference to one of its owning classes via <co=
de>static_cast</code>. But implicit conversion is not allowed.</p>
<h3 id=3D"inner_impl">Implementation</h3>
<p>The fundamental operation of inner classes is the (static) transformatio=
n from a pointer to the inner class type to a pointer to its direct owning =
class type. The implementation of this operation has to be done separately =
for two cases: one for stateless inner classes and one for non-stateless in=
ner classes.</p>
<p>The stateless version is based primarily on the fact that pointers to st=
ateless classes could point to anything. Including... directly at their own=
ing class instance. So the memory location of a stateless pointer should al=
ways be the same memory location of their owning class. If their owning cla=
ss is itself a stateless inner class, then this propagates up the ownership=
hierarchy to the first non-stateless inner class or non-inner class.</p>
<p>Note that if the owner is a stateless non-inner class, then the memory l=
ocation for him should be whatever the compiler deems convenient.</p>
<p>There is a snag that appears when a stateless inner class is a member of=
a derived class. In this case, the compiler needs to recognize that the ow=
ning class is a base class of its container. This is a static property, so =
it is easy to know about. Once the compiler recognizes that, it can assign =
the pointer/reference by doing a <code>static_cast</code> to the base class=
, then converting that memory location to the inner class type.</p>
<p>This becomes more complex with the following:</p>
<pre><code>class Base
{
public:
inner stateless struct In1
{ int GetBase() const {return baseVar;} };
private:
int baseVar;
};
class Derived : public Base;
{
public:
inner stateless struct In2 : public Base::In1
{
int GetBase2() const {return GetBase();}
};
=20
In2 acc;
=20
private: =20
int deriVar;
};</code></pre>
<p>The key to remember here is that both <code>Base</code> and <code>Derive=
d</code> have non-stateless NSDMs, so they both take up space. <code>Derive=
d</code> is not standard layout, and therefore the pointer to <code>Base</c=
ode> need not have the same memory location as a pointer to derived<code>De=
rived</code>.</p>
<p>Even with this complexity, things still work. For <code>Derived::In2::Ge=
tBase2</code>, the compiler recognizes that <code>GetBase</code> is in the =
base class of <code>Derived::In2</code>. But it also realizes that this bas=
e class is an inner class, who's owner is a base class of this inner class'=
s owner.</p>
<p>So it converts the current <code>this</code> pointer of type <code>Deriv=
ed::In2*</code> into a <code>Derived*</code> (changing only the type), then=
converts it into a <code>Base*</code> (possibly changing the memory locati=
on), then converts it into an <code>Base::In1*</code> (again changing only =
the type). The same thing happens for calling <code>acc.GetBase()</code> di=
rectly.</p>
<p>All of these conversions are static, based on compile-time offsets. They=
do not rely on a particular instance of any of the stateless inner classes=
..</p>
<p>Stateful inner classes are more complex. This is because each stateful c=
lass instance must have a separate address from every other stateful NSDM. =
Because each member has its own state, the conversion from <code>Outer::Inn=
er*</code> to <code>Outer*</code> is no longer a simple typecast or a stati=
c pointer offset. And if we want to be able to pass around references to st=
ateful inner classes, we need to be able to, from the inner class's <code>t=
his</code> pointer alone, convert it into a pointer to the owning class.</p=
>
<p>Therefore, for each stateful inner class member, the compiler must have =
access to some data (typically a byte offset) which is used to convert <cod=
e>this</code> from <code>Outer::Inner*</code> to <code>Outer*</code>. For m=
ultiple nestings of stateful inner classes, each level has its own offset t=
o get to the next level.</p>
<p>The question is where this offset is stored.</p>
<p>One thing is certain: the offset must be stored someplace that is access=
ible from just a pointer to <code>this</code>. After all, users may have on=
ly a pointer/reference to an inner class member, and they have every reason=
to expect that this will be a working pointer/reference. In such a case, t=
he only pieces of information the compiler has are the class definitions an=
d <code>this</code>.</p>
<p>We are left with two alternative implementations. The offset could be st=
ored within the inner class itself, in a hidden member (ala vtable pointers=
). Or the offset could be stored in the direct owning class, in memory that=
is directly adjacent to the instance.</p>
<p>Both of these have behavioral consequences. Both solutions break standar=
d layout, as a consequence of having to insert additional data. But this is=
to be expected. The question is what functionality each option allows and =
what it forbids.</p>
<p><strong>Option 1: In-Object Memory:</strong> This is the most obvious im=
plementation strategy. The offset is simply a hidden field of the inner cla=
ss.</p>
<p>The downside of this approach is that stateful inner classes <em>cannot<=
/em> be trivially copied. This is for the same reason that classes involvin=
g virtuals cannot be trivially copied. The value of the offset is based on =
the specific member variable and where it is within its owning type. As suc=
h, copying it between two <em>different</em> members (of the same type) is =
really bad. And since the offset is part of the type, you can't copy the tw=
o types with a simple <code>memcpy(dest, src, sizeof(T))</code>. And theref=
ore, they are not trivially copyable.</p>
<p>The odd part here is that, while inner classes themselves are not trivia=
lly copyable, the class containing inner class members still can be trivial=
ly copied. This works because the data is an offset, not a pointer directly=
to the owner. As such, trivial copies can work if they are they are betwee=
n the same variable (the variable defines the offset). Which they will be i=
f the copy is between to instances of the containing class.</p>
<p>So this option retains trivial copyability for the container, at the exp=
ense of trivial copyability for the members. And it makes the rules more co=
mplex.</p>
<p>Also, the containing class cannot be <em>trivial</em>. This is because t=
he stateful inner class members are also not trivial; their default copy co=
nstructors must initialize this hidden data. And the container must provide=
them with the hidden offset to initialize it with.</p>
<p><strong>Option 2: Adjacent Memory:</strong> The offset could be stored i=
n the containing class as a hidden value. The offset would be stored direct=
ly before its associated inner class.</p>
<p>If the offset is adjacent to the object, then fetching the offset with o=
nly <code>this</code> is easy: simply increment/decrement the pointer so th=
at it points to the adjacent memory. The class definition will tell the com=
piler if it is stateful, and if it is, then it knows it needs to do this wo=
rk to get the offset.</p>
<p>The adjacent memory method preserves trivial copyability of both the inn=
er and containing classes, because the offset is where it needs to be: asso=
ciated with the type that actually defines that offset. The offset is defin=
ed by the arrangement of the members of the outer class. So the outer class=
is the one that stores the offset. The outer class can still be trivially =
copied because the offset for each member is a static property, not a dynam=
ic one.</p>
<p>The outer class cannot be <em>trivial</em> however, since its default co=
nstructor (and every other non-trivial-copy/move constructor) will need to =
initialize this hidden data.</p>
<p>The downside of this choice is that it breaks the ability to have arrays=
of stateful inner class members. The reason being that arrays in C++ are r=
equired to be contiguous, that the pointer distance from one array element =
to another must be exactly <code>sizeof(Class)</code>.</p>
<p>Note that if we forbid arrays of stateless classes period, forbidding ar=
rays of state<b>ful</b> inner classes is not far afield.</p>
<p>The question is which is more useful: simpler trivial copyability rules =
and trivial copyability for inner classes vs. aggregating arrays of statefu=
l inner classes? This proposal focuses on the former.</p>
<h3 id=3D"other-implementation-based-restrictions">Other Implementation Bas=
ed Restrictions</h3>
<p>You will notice quite a lot of places where virtual inheritance is forbi=
dden. This is done to ensure that all of the pointer conversions remain <em=
>static</em> operations. Virtual inheritance would require dynamic operatio=
ns, where a simple pointer offset is not possible.</p>
<p>Strict aliasing in C++ says that two unrelated pointers point to differe=
nt objects. However, inner class members are related to one another, even i=
f they are technically siblings. So the definition of "related" n=
eeds to be expanded to cover inner classes.</p>
<p>Specifically, inner class types are related to the class they are declar=
ed within. Recursively up to the first non-inner class.</p>
<p>You may also notice that this design explicitly does not allow the more =
open Java syntax. That is, there is no way for arbitrary code to dynamicall=
y allocate inner classes to some object. This is primarily to reduce the im=
plementation burden. We would need to not only add entirely new syntax for =
it, we would need to allow binding of inner classes to smart pointers to th=
eir owning types or similar constructs.</p>
<h2 id=3D"mixin-classes">Mixin Classes</h2>
<p>The syntax for mixin declarations is somewhat in question at present. Th=
e general ideas for the syntax is to:</p>
<ol style=3D"list-style-type: decimal">
<li><p>Make being a mixin a first-class property of the type, which is asso=
ciated wtih the type's declaration. This makes it possible for the compiler=
to verify that the user is using the mixin type correctly.</p></li>
<li><p>Rather than arbitrarily deciding which template parameter is the der=
ived class type, allow the user to specify it explicitly. This makes the sy=
stem easier for users to use, allowing the template argument interface to b=
e what is most natural for the type.</p></li>
<li><p>Allow the directly derived class to not have to type its classname d=
irectly into the base class instantiation. This prevents user error, as wel=
l as making it abundantly clear to the reader that a mixin is being used.</=
p></li>
<li><p>Prevent users from accidentally putting the derived class type in th=
e wrong location in the argument list. That is, both the derived class and =
the mixin implementation must agree which template argument is the mixin's =
owning type.</p></li>
</ol>
<h3 id=3D"class-declaration">Class Declaration</h3>
<p>A mixin class is a template class that is declared as follows:</p>
<pre><code>template<parameters>
class name std::mixin<parameter>
{
...
};</code></pre>
<p>The <code>parameter</code> associated with the <code>mixin</code> keywor=
d must be the name of one of the <code>parameters</code> listed in the temp=
late argument list. This parameter must be a typename (it can be constraine=
d by a concept). The specified template parameter becomes the owning type f=
or the mixin.</p>
<p>The <code>parameter</code> may not be any form of expression; it must be=
exactly the name of a template parameter.</p>
<p>The <code>mixin</code> part of the class declaration must be a part of a=
ny forward declarations of the class.</p>
<p>Mixin classes obey all the rules of templates and classes, except where =
noted below.</p>
<p>The standard library should have a template metafunction (and variable) =
for testing if a type is an mixin class.</p>
<h3 id=3D"type-usage-1">Type Usage</h3>
<p>Mixin template classes can be explicitly instantiated, but no variables =
of their types can be declared. Objects of mixin types can only be created =
as base subobjects of other classes. Virtual inheritance of mixin classes i=
s not allowed. Special syntax must be used when deriving from a mixin class=
as well:</p>
<pre><code>template<typename Mixin
class name std::mixin<Mixin>
{ =20
...
};
class derived : public name<std::mixin>
{
};</code></pre>
<p>Here, the <code>mixin</code> keyword is used in the template instantiati=
on syntax to refer to the specific template parameter that was specified to=
be the mixin type. The template will be instantiated as if <code>mixin</co=
de> was replaced by <code>derived</code>. If the mixin has multiple templat=
e parameters, the others can be filled in as normal. The <code>mixin</code>=
keyword must be applied to the template parameter that was declared to be =
the mixin parameter in the template declaration.</p>
<p>Template substitution works exactly as if the user had used <code>name&l=
t;derived></code>.</p>
<p>Not using the <code>mixin</code> keyword with a mixin class template is =
an error, even if it correctly names the derived type:</p>
<pre><code>class mine : public name<mine> //Not allowed
{
};</code></pre>
<p>Similarly, when deriving from non-mixin types, the use of the <code>mixi=
n</code> keyword in the template argument list is not allowed. Also, if the=
<code>mixin</code> keyword is used in place of a parameter that is not the=
mixin parameter, a compiler error results.</p>
<p>When naming the concrete mixin base class <em>within</em> the definition=
of members of the direct derived class, <code>mixin</code> must be used in=
the template argument list. Outside of members of the derived class, the u=
ser must name the derived class explicitly.</p>
<p>Pointers and references to mixin classes can be used as normal. Mixin ba=
se classes operate as normal base classes do, except where mentioned below.=
</p>
<h3 id=3D"mixin_aliases">Type Aliases and Mixins</h3>
<p>Aliases for mixins work unusually. A mixin must always have a template p=
arameter specified as the mixin's owning type. And aliases are no exception=
.. Therefore, <code>typedef</code> cannot be used to create a mixin alias, o=
nly <code>using</code> syntax.</p>
<p>The <code>using</code> declaration must have at least one template param=
eter, and the syntax must specify which template parameter is the mixin's o=
wning type. Furthermore, that parameter must be used in the alias itself. H=
ere is an example:</p>
<pre><code>template<typename T, typename derived>
class old_mixin std::mixin<derived>;
typedef old_mixin<int, some_class> bad1; //Illegal, no template param=
eter for the mixin type.
template<typename T>
using bad2 =3D old_mixin<T, some_class>; //Illegal, mixin type must r=
efer to a template parameter.
template<typename derived>
using bad3 =3D old_mixin<int, derived>; //Illegal, new alias must spe=
cify which parameter is a mixin.
template<typename T, typename derived>
using bad4 std::mixin<T> =3D old_mixin<T, derived>; //Illegal, =
the specified mixin parameter is not passed to the original type's mixi=
n parameter.
template<typename derived>
using correct std::mixin<derived> =3D old_mixin<int, derived>; =
//Works</code></pre>
<p>Mixin template parameters can be constrained by concepts, but they must =
still have a type parameter.</p>
<p>The reason why mixin aliases must always have a template parameter for t=
he mixin type is because of the syntax used for deriving from a mixin. The =
current syntax has the user put the <code>mixin</code> keyword in the templ=
ate argument list, which not only fills in the class but tells the compiler=
to make sure it's a mixin.</p>
<p>If we want to have cases like <code>bad1</code> or <code>bad2</code> wor=
k (those mixins could only be used with <code>some_class</code>), it is sti=
ll important that the user deriving from the class make it clear that they =
are aware that it is a mixin. Because inheriting from a mixin <a href=3D"#m=
ixin_scope">implicitly makes the mixin a friend of the class</a>, it is ver=
y important to make it clear to the user at all times that a mixin is being=
derived from.</p>
<h3 id=3D"mixin-specializations">Mixin Specializations</h3>
<p>If one specialization of a template class is not a mixin, then all speci=
alizations must not be mixins.</p>
<p>Similarly, if one specialization is a mixin, all of them must be mixins.=
A mixin specialization may not declare a concrete class to be the mixin ty=
pe. The parameter may be constrained via concepts, but it must always be a =
template parameter.</p>
<p>The reason for this limitation is similar to the <a href=3D"#mixin_alias=
es">one for aliases</a>. If we allow specializations for a specific mixin b=
ase/derived class pairing, there would need to be some way for the user of =
the mixin to provide some idea that they intend to derive from that mixin.<=
/p>
<h3 id=3D"mixin_scope">Inversion Scope</h3>
<p>Scoping for mixins is a bit more complex, due to the fact that inheritan=
ce access classes (public/private/protected) scope how far a mixin can reac=
h.</p>
<p>A mixin instantiation within a class hierarchy has reach through every m=
ixin that derives from it, up until one of the following is reached:</p>
<ul>
<li>A non-mixin class</li>
<li>A mixin class that used private inheritance on this branch of the tree<=
/li>
</ul>
<p>All classes between the mixin class and this class (including that class=
) represent the "scope" of the mixin.</p>
<p>Every class within the mixin's scope is implicitly friends with the mixi=
n.</p>
<p>Within member functions of mixin classes (except for constructors and de=
structors), the following rules are added.</p>
<p>Members (static or not) of an mixin class may access members of any inst=
ance of its owning instance through a pointer/reference to its own class (i=
ncluding implicit <code>this</code>) as though it were derived from that cl=
ass. Name lookup takes place as though the owning class were placed at the =
end of the mixin class's list of derived classes. Therefore explicitly deri=
ved classes take priority over owning ones. Name lookup propagates up throu=
gh the entirety of the mixin's scope.</p>
<p>Qualified lookup within mixin member functions can also be performed up =
the ownership hierarchy as well. Similarly, mixin classes may implicitly co=
nvert pointers/references to their own types into pointers/references to th=
eir owners, up through the mixin's scope.</p>
<p>Note that the above properties are also conferred onto any lambda functi=
ons created by such members. Friends of mixin classes may gain these abilit=
ies as well (mainly to allow lambdas to work). Non-friends of mixin classes=
cannot do these.</p>
<p>The ability of non-friends to perform such conversions depends entirely =
on the access classes of those non-friends and of the inheritance diagram. =
Or to put it another way, the rules of casting up and down the hierarchy re=
main unchanged for non-friends of mixins.</p>
<h3 id=3D"possible-additions">Possible Additions</h3>
<ul>
<li>Given a mixin class, syntax could be added to get the derived class typ=
e. Perhaps <code>mixin<mixin_type></code> would resolve to a typename=
..</li>
</ul>
<h3 id=3D"mixing-mixins-and-inner-class">Mixing Mixins and Inner Class</h3>
<p>Inner classes and mixins propagate access and friendship up to the first=
non-inner class or non-mixin (or the first private-inheritance mixin). But=
because they are really the same concept implemented in different ways, th=
ey should interact well together.</p>
<p>Therefore, if an inner class derives from a mixin non-privately, the mix=
in's scope will be extended through the inner class. And if a mixin declare=
s an inner class, the same applies: that inner class's scope extends throug=
h the mixin.</p>
<p>So if you have this:</p>
<pre><code>template<typename derived>
struct M2 mixin<derived>
{
inner struct I : M1<mixin>
{
int val_i;
} in;
=20
int val_m2
}
struct T : public M2<mixin>
{
int val_t;
};</code></pre>
<p>In instances of <code>T</code>, functions from <code>M2<T>::M1<=
I></code> will be able to access <code>val_i</code>, <code>val_m2</code>=
, and <code>val_t</code>, in that order.</p>
<p>This feature allows users to use mixins to write the implementation of i=
nner classes outside of the containing class. Thus the guts of an implement=
ation can be used in multiple classes, while still allowing tclass-specific=
tweaks to be provided by the inner class using the mixin. For example, <co=
de>I</code> could provide its own <code>val_m2</code> that shadows what <co=
de>M1</code> can access. And because <code>M1</code>'s implementation is ig=
norant about the world outside of the mixin that it is declared within, it =
has no way to bypass this shadowing.</p>
<h1 id=3D"remaining-issues-to-resolve">Remaining Issues to Resolve</h1>
<ul>
<li><p>Arrays of stateless types: allowed or forbidden?</p></li>
<li><p>Stateful inner class trivial copyability vs. declaring arrays of inn=
er classes.</p></li>
<li><p>How inner classes work with pointers-to-members.</p></li>
<li><p>Mixin aliases & specializations. Currently, the mixin owning typ=
e must always be a template parameter, preventing complete mixin specializa=
tions for specific types. Should we allow full specialization?</p></li>
<li><p>The greed of scoping rules. For example, consider the <a href=3D"#us=
es_properties">suggested property implementation</a> previously stated. The=
property mixin would have access to the inner class, but it would also be =
able to access the owner of the inner class. Which means that it could acci=
dentally mistake one of the owner's members for that of the inner class.</p=
>
<p>For inner classes, arbitrary reach makes sense, as they are effectively =
pieces of some other class. But for mixins, there probably should be some w=
ay to explicitly declare that their scope ends with the class that uses the=
m, even if it is an inner class. Without using <code>private</code> inherit=
ance, that is, as this would prevent the derived class from making its inte=
rface public. This could be done at derivation time, with a parameter passe=
d to the <code>mixin</code> keyword: <code>class derived : public mix<mi=
xin: public></code> or something of the like.</p></li>
</ul>
<div class=3D"footnotes">
<hr />
<ol>
<li id=3D"fn1"><p>Implementation note. For stateless inner classes in deriv=
ed classes, the pointer to the stateless class has to have the same value a=
s the base class. So special pointer gymnastics are needed when getting poi=
nters/references to such variables. For stateful inner classes, the offset =
simply needs to be able to jump out of the derived class type and into the =
base class.<a href=3D"#fnref1">=E2=86=A9</a></p></li>
</ol>
</div>
</body>
</html>
------=_Part_10243_2020340158.1446141886863
Content-Type: text/x-markdown; charset=US-ASCII;
name="Mixins, Inner, and Stateless Classess.md"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Mixins, Inner, and Stateless Classess.md"
X-Attachment-Id: 08c0a543-a4a8-472a-89df-8cba21c3ddee
Content-ID: <08c0a543-a4a8-472a-89df-8cba21c3ddee>
% Mixin, Inner and Stateless Classes
%
% October 29, 2015
This proposal specifies three new language features. Two of these features are really two halves of the same conceptual coin (though they are separate features). The third feature is an optimization for certain use cases that make the other two features much more attractive.
# Concepts
## Inverted Subobject
A subobject is itself an object, but it is wholly controlled by the object in which it is contained. For the purposes of this discussion, we will consider two kinds of subobjects in C++: non-static data members (NSDMs) and base class members. An NSDM adds to the capabilities of the object to which it is a member by containment. Each NSDM is accessed independently, but it is still fundamentally dependent on the object it is contained within. Base class subobjects are similar in that they expand the capabilities of the containing class. However, they expand those capabilities directly, bringing all of their functionality into the direct interface of the derived class (as appropriate to the derivation protection class, of course).
With subobjects of all kinds, control flows in one direction: from the containing object to the objects it contains. Base classes can access their own members as well as any members of one of their base classes. But no base class can (directly) access members of its derived class. Similarly, NSDMs can access any NSDMs of their own, but they cannot access the interface of the object that contains them.
What this proposal suggests doing is changing this relationship. An "inverted subobject" is one where the relationship between the container and the contained objects are inverted. Inverted base subobjects can access their (direct) owning object, and inverted NSDM subobjects can access the object that they are contained within.
It turns out that both of these kinds of control inversions are in use today, either in C++ itself or in other languages. While conceptually these are just variations of the same thing, these features are often implemented as two distinct features with two distinct names.
Java provides the name for an NSDM that can access its owning class: inner class. The other concept's name is defined by the effects of the concept. If a base class can access the derived class's members, then it can add functionality to that class which integrates seamlessly with any particular implementation that provides the appropriate interface. Let us refer to this as a mixin.
Note that mixins effectively allow a form of static polymorphism. The normal way to invert relationships in a hierarchy is to use `virtual` functions. The derived class implements the virtual interface declared by the base class. But this can be inefficient, due to the overhead of virtual calls.
Mixins effectively allow the same inversion in a static way. Not unsurprisingly, mixins in C++ are usually implemented using the static polymorphism tool of C++: templates.
# Current Implementations
Inner classes and mixins can be implemented in the language as it currently stands. The question is what kind of gymnastics are required to implement them, as well as the ability to compose them from different library implementations. And of course how easy it is to get the implementation wrong.
## Mixin
For our purposes, mixin implementations should allow:
1. The mixin class to access the members of the class it "mixes" into. How much access is a question, but even just public access would be functional.
2. The class being mixed into can access the members of the mixin. Again, just public members would be something.
3. Users of the class being mixed into should be able to access public members of either class through an instance of the containing class.
4. Multiple mixins can be applied to the same class (to the degree that their interfaces do not conflict). And separate mixins into the same type can effectively communicate with one another.
5. Mixin composition. That is, building a new mixin which is the sum of itself with a second mixin.
The rules of C++ do not allow a concrete class to be able to call functions it does not know about. And since we want mixins to be arbitrarily compose-able, a mixin cannot know what it is derived from. The obvious solution here is a template of some kind.
There are two general strategies for building mixins in C++. One relies on explicitly inverting the class hierarchy, while the other involves using a regular hierarchy and clever casting tricks. They each have their drawbacks.
### Hierarchy Inversion
In this case, mixins are implemented by having the mixin be the *derived* class. Each such mixin looks something like this:
template<typename Base>
struct mixin : public Base
{
//Add members here.
};
Users in this case do not use the `Base` class directly. They use the fully composed mixin type, which is built from a nested template sequence, usually hidden in an alias:
using MyClass = mixin1<mixin2<mixin3<Base>>>;
This allows mixins to have access to `Base` through a template interface. Concepts can be used to specify the particular interface needed.
The downsides of this approach are many. It does not fulfill #2, as the base class cannot directly communicate with any mixins that it uses. This is an acceptable limitation if the class's main functionality is built from mixins. But if a mixin is being used to extend the class's functionality, this hierarchy inversion becomes untenable.
It also makes it impossible to have constructs like private mixins, where the mixin provides some facility that is merely used by the `Base` class without being exposed to external code. Yet this it still needs to be a mixin, since that functionality requires the inverted relationship. So these mixins are only useful for public interfaces.
It also makes #3 difficult, as users must name the entire mixin chain when naming the type. Obviously a typedef makes this work just fine, but it does make the ultimate typename rather cumbersome. It also means that `mixin1<mixin2<Base>>` is a fundamentally different type from `mixin2<mixin1<Base>>`, despite being functionally identical.
On the plus side, it makes it easy for two mixins to communicate effectively. Granted, the communication can only proceed in one direction. But mixin composition works.
Another positive of this approach is that it's very simple and difficult to get wrong. It relies on normal inheritance relationships in C++. Inversion is achieved simply by deriving in the opposite direction.
### CRTP
The curiously reoccurring template pattern (CRTP) can be used for a more featureful mixin. The way this works is simple. The mixin is a template, which is passed the *derived* class as a template argument. Thus, the derived class inherits the interface of the mixin. But because the mixin knows its derived class type, it is able to perform a `static_cast` of its `this` pointer to that type. And thereby access the derived class's members.
Here is an example implementation of this:
template<typename derived>
class mixin
{
public:
void caller() const
{
auto _this = get_this();
_this->some_function();
}
private:
derived *get_this() {return static_cast<const derived *>(this);}
const derived *get_this() const { return static_cast<const derived *>(this); }
};
class destination : public mixin<destination>
{
public:
void some_function() {...}
};
This kind of mixin satisfies all of the requirements. There is full cross-talk; the derived class can access the mixin and vice-versa. Though they can only access the public members of each, that is an acceptable limitation. Users of the derived class can access the mixin's public members (assuming public inheritance) as though they were members of the derived class. And thanks to multiple inheritance, it is easy for one type to use multiple mixins. Indeed, mixins can be composed with one another.
The most obvious problem is the inconvenience. The need to convert the `this` pointer manually is a particular annoyance. But we don't necessarily add language features for simple convenience. So instead, let us look at where this pattern breaks down.
First, it is difficult to compose mixins through inheritance. That is, if we have one mixin `A`, and create a second mixin `B` which is derived from `A`, it can create a problem for `B`. Consider this:
template<typename derived>
class A {...};
template<typename derived>
class B : public A<B<derived>> {...};
If `A` has some interface that it expects from `derived`, then by the way this interface works, `B` *must* expose that interface. However, since `B` is itself a mixin, the idea should be that `B` *inherits* the interface of `A`. That is, whatever interface `A` requires becomes part of `B`'s interface requirements too. `B` of course has the opportunity to implement that interface, but it should not *have* to.
Above, mixins were analogized to dynamic polymorphism, and that helps demonstrate the limitation with the mixin version. If `A` had pure-virtual member functions, `B` would not necessarily have to implement them. If it does not, then `B` becomes an abstract class, and users of `B` must implement whatever pure-virtual members that `B` does not implement.
If mixins are to be the static equivalent, then they should provide the same functionality. Yet the CRTP version of mixins doesn't allow this. `B` must provide the entire interface. It can forward those calls to its own `derived`, but it must do this explicitly for every function (or variable) that `A` requires.
The CRTP implementation has no way to check for mistakes. Here are some things you have to do with such mixins that are not (directly) checked for:
* Use the correct type when deriving from them. The compiler will catch the error in the implementation, but only because it's instantiating the template and can't find the interface it expects. So the compilation error will be far from the actual problem.
* Not use virtual inheritance. That breaks the `static_cast`. The compiler can't check for that one.
* Never declare objects of mixin types directly. That is, you cannot write this declaration: `mixin<SomeType> a;`. The compiler won't catch that either; the `static_cast` will assume that the mixin is derived from `SomeType`. And if it isn't, then you'll get odd runtime failures.
So the CRTP implementation is somewhat fragile.
One final problem has to do with the size of the resulting object. Many mixins have no NSDMs. One of the main purposes of mixins is to allow the contained object to provide the storage for some property, or to otherwise simply modify the interface of the contained type.
Even so, such objects can impact the size of the resulting object. If the contained type was standard layout, then empty base optimization takes care of the issue. But if it was not (perhaps the containing object has virtual functions, or some other mixin brings in its own members), then compilers are free to not optimize empty bases. And therefore, you cannot rely on such optimizations. If class size is of importance to you, every mixin potentially could be making your class bigger.
Needlessly.
## Inner Class
The concept of Java-style inner classes needs some explanation, as it is very unlike normal C++ classes.
In C++, if you declare a class within another class definition (nested classes), the nested class has absolutely no relationship with the class that it is declared within. It is not even a friend of it, let alone a part of it. In C++, nested classes exist solely for scoping of the class's name.
In Java, a nested class is implicitly the friend of the class they nest. This allows nested Java classes to act as part of the class's interface. But a non-static nested class is even more special; it is an inner class.
Java inner classes implicitly allow their own `this` pointer to convert into a pointer to the instance that their current object instance is a member of. And since they are implicitly friends, they have direct access to all members of their containing instance. So if there is an instance `a` of a class `A`, and it has an inner class member `b`, then any non-static member function of `b` could call non-static member functions in `a`. Not just in the class, but in the *specific instance* `a` which holds this particular `b` instance object.
One can pass Java inner class members around to other functions, which can call functions on those inner class members. The members that get called can themselves still access `a`, even though the function that called them was passed `b`.
In terms of implementation, what is needed is a way to transform `b`'s `this` pointer into a pointer to its owning object of type `A`. In Java, this is done by literally sticking a hidden pointer into the inner class type; garbage collection handles lifetime issues. This also allows Java inner classes to be declared outside of the class they are nested within. The code allocating it still has to have an object of that type, but the variable is not directly a part of the owning class.
Without generalized garbage collection, C++ can't really do that. As such, implementing inner classes in C++ is difficult. But this is not impossible. It does require quite a bit of help, though.
The general idea is to first restrict inner classes to being NSDMs of their containing instances. Then, have the containing class's constructors (all of them, so kiss trivial copyability goodbye) pass a `this` to every inner class member as part of those members' construction. Each inner class member stores a copy of that pointer. When an inner class member is being copied, care must be taken not to copy the `this` member (so the inner class itself can't be trivially copied either).
These kinds of implementations are functional. But unlike mixins, it's a lot harder to ignore the difficulties of implementation. *Every* constructor of the owning class has a bit of boilerplate in it, making the whole process quite fragile. Every constructor of the inner class has to store the `this` pointer correctly.
Also, the implementation causes problems for the owning type. Because of the boilerplate code, the owning type cannot be a trivially copyable class.
Composition of inner classes (inner classes of inner classes) is problematic, for the same reason as the CRTP-based mixin implementation. Each inner class gets to access its direct outer class, but no outer classes beyond that without even more special coding. Though at least in this case, because inner classes are concrete classes, it is at least possible for the inner class to read the outer inner class's pointer to *its* outer class. But that requires that the user manually determine which function in which class in the inverted hierarchy needs to be called and then use the correct pointer to make that call.
Another problem is that you cannot make inner classes who's definitions come from elsewhere. So you cannot make a library of inner classes that others use inside their classes.
Oh, and while empty base optimization is sometimes possible for mixins, there is no allowance in C++ for empty *member* optimizations. So every empty inner class member bloats the class. Though the fact that "empty" inner classes still need that `this` pointer means that they're not empty in *practice*, just in concept.
## Stateless Optimization
There are many uses of these inverted subobjects. However, quite a few of them involve the inverted class having no NSDMs at all. The state these objects manipulate or access is provided by the owning class, with the mixin/inner class merely providing an interface.
The problem is that, by the rules of C++, all members do take up space in the final class. While empty mixins are required to take up no space when used as bases of standard layout types, there is no such optimization for non-static data members. And even with base classes, if the owning class cannot be standard layout, then removing this space is entirely optional.
This issue has been one of the main reasons why people do not find some library implementations of properties attractive (implemented as a very limited form of inner classes). Making the interface slightly nicer to use is usually not worth the overhead. Therefore, finding a way to remove this overhead would be ideal, and would for many people be considered a necessary first step.
As such, this proposal includes not only the two inverted subobject types, but a third feature that both of the other two can make significant use of. This is the ability to declare that an empty class shall not take up room when declared as a subobject of another type.
# Uses
The uses of full mixins are legion. And the ability to make them stateless where appropriate is the icing on the cake, as it allows the containing class to provide data storage for the mixin's interface. For example `enable_shared_from_this` effectively stores a `weak_ptr` (or the guts of one) in the class. If it were a true mixin, it could allow the class implementation to store the `weak_ptr` in a data structure of its choosing. This would also allow the class to access the `weak_ptr` directly, whereas currently you have to do `weak_ptr<T>(shared_from_this())`, which involves a needless quantity of atomic operations.
And all of this comes without virtual function overhead.
Stateless classes which are not inverted have limited utility, but they still retain some. For example, iterator categories are tested currently via the presence of certain base class tags. These tags have no state besides the fact that they exist. These and similar types are reasonable use cases for statelessness without inverted classes.
It is inner classes where their utility requires some justification, as they are rarely seen in C++.
## Properties {#uses_properties}
Full disclosure: I am personally not a big fan of properties. However, I also know that this is a thing people want, and the thread/proposals that led to me developing this one were primarily about getting properties into the language somehow. Obviously, this feature has evolved since then.
Stateless inner classes make it possible for users to implement properties with zero overhead. Prior property implementations also seemed really hackey; they would require things like lambdas as initializers to some struct, or the property itself would have to hold the value (thus making it impossible for properties to represent things that were not values). With stateless inner classes, the property implementation has full access to most of the powers of the C++ object model, without a bunch of boilerplate to access the data.
They can overload `operator=` to perform assignment as the "setter" operation. They can overload `operator Typename` for implicit conversions as the "getter" operation. If the underlying value is of an appropriate type, they can even overload math assignment operators like `operator+=` and such. In effect, the property object can be a full proxy for the real thing, providing access, modification, or both, in all of the ways that C++ allows. And all without taking up added space.
Because there is a lot of boilerplate here to make a good property that plays by C++'s rules, this boilerplate can almost certainly be abstracted out... into a stateless *mixin*. The inner class property itself would be responsible only for providing a `get` function and a `set` function. The mixin is what would do all of the work for operator overloading and such.
Template polymorphism can be used here as well to improve the interface. For example, if the return value of `get` is a pointer (or a type that implements `operator->`), the property can synthesize an appropriate `operator->` overload. If no `set` method is provided, then the property does not synthesize assignment operators. If no `get` method is provided, the mixin does not allow data access. But more complex rigging can be accomplished as well.
If there is a non-const `get` overload that returns a reference, and there is no `set` method, then the mixin assumes that `get() = expr` is an acceptable means of changing the value. So its overloaded assignment operators will be synthesized by calling thenon-const `get`. Furthermore, a non-const `get` allows the system to synthesize the `operator+=` and similar operators without having a `get`/`set` round-trip.
And thus, a property definition in a class would look like this:
stateless inner struct : property<mixin> //Anonymous struct definition.
{
float &get() {return value;} //non-const reference means `property` will use this to set as well.
const float &get() const {return value;}
} property_name;
The mixin does all of the complex work that synthesizes the appropriate operator overloads. The prefix part could even be wrapped up in a macro.
## Proxies and Interface Translation
Properties are really just a specific instance of a more general use case for stateless inner classes: interface translation without creating new proxy objects.
Let's say that you have some container class `C`. It has an interface that works for you and your needs. But with the ranges proposal, you know that it doesn't really fit into that paradigm. It is conceptually a range, but it doesn't actually fit the range paradigm. And you don't want to rewrite your interface; if it's not broken, don't fix it.
The way to resolve that today would be to create some proxy object `PC`, which internally stores a pointer/reference to a `C` instance. This object would translate the interface of `C` into one appropriate for the ranges proposal.
A more pressing concern is one of lifetime. Because `PC` is an object type, which the user can declare values of, it is now possible to take a `PC` and possibly store it long term. And since it stores a pointer/reference to `C`, that makes it way too easy for someone to think that they have a real object rather than a proxy.
If the proxy object is an inner class instance however, the proxy *cannot* be stored. A reference to it can be stored, but then it is abundantly clear that you're storing a *reference*, not a container-like object. And thus, it's clear to everyone that you have stored a reference to some other object, which is what virtually every proxy object is.
It also alleviates the small overhead to using proxies. A regular proxy object has to be constructed every time a proxy is created. Whereas an inner class proxy is a fixed part of the class, rather than generated as needed by the user. It can be stateless, with all of the data it's accessing stored within its owner. This saving an indirection, since getting the pointer to its owner is a static operation rather than a memory access.
Proxy iterators are a good place to see where this overhead could be non-trivial. Particularly in proxy output iterators, every time you do `*it++ = ...`, you call the proxy's constructor, which copies a pointer/reference to whatever is needed into the proxy object. It gets used once to do the copy, then destroyed.
With inner classes, the proxy type can be a stateless inner class of the iterator type itself. So `operator*` returns a reference to the inner class. The inner class's `operator=` will store the value, possibly updating the position of the iterator. And this neatly avoids having to create and destroy an object every time insertion happens.
Aggressive compiler optimization may alleviate some of these concerns, but inner classes ensure that they don't exist.
This kind of translation also makes it possible to provide different ways to view the same data. An example used to illustrate this was a class that stores colors. The class would internally store the color in the sRGB colorspace, but through a stateless inner class interface, you could modify the value in the HSV colorspace.
Without inner classes, this would require a bunch of explicit calls to conversion functions: `color.set_hsv(color.get_hsv().set_hue(new_hue))`. With stateless inner classes, it requires `color.hsv.hue = new_hue`. This is much cleaner and clearer to the user. And probably more efficient.
One could even do `color.hsv.hue += new_hue`. Without the inner class, a similar operation would have either required an explicit `add_hue` function or the user would need to do this:
auto &&hsv = color.get_hsv(); //Avoid double get call.
color.set_hsv(hsv.set_hue(hsv.get_hue() + new_hue));
This takes up two lines now. If you accept the double get, you get the gargantuan expression:
color.set_hsv(color.get_hsv().set_hue(color.get_hsv.get_hue() + new_hue)
And remember: all of these inner classes can be *stateless*. All they do is enhance the class's interface; they impose no overhead on their containing classes.
Not every set of types with different representations should use such views. For example, [Howard Hinnant's Date/Time library](https://github.com/HowardHinnant/date) provides `day_point` and `year_month_day`, which are two versions of the same data. I would not suggest giving `day_point` a view that provides `year_month_day`'s interface; they should be two separate types, due to the conversion overhead.
Though it could still have property-like objects for setting/getting dates in different formats.
# Design
Now that we have discussed what these features are, their limitations with current implementations, and what we could gain from putting them in the language, let's talk about how to add them into the C++ language. Here are the goals for the overall design of these features:
* Inverted and stateless classes should have as few restrictions on their use and nature as is implementable. At least, those restrictions that are not based on their design. For example, inner class instances are directly associated with an instance of their containing class. As such, it makes no sense to be able to declare automatic variables of them or put them on the heap, so restricting them to being declared as members of that class is acceptable. However, it should be perfectly reasonable to pass around references or pointers to such objects.
* If a class contains inverted subobjects, the class should be impacted by this as little as possible, except where obvious. In particular, we should try as much as possible to avoid complicating standard layout and trivial copy-ability rules, unless it is absolutely necessary to the implementation.
A word about syntax. C++ makes adding new keywords difficult. However, P0056 suggests a way to make adding keywords rather painless. Therefore, this proposal will be written assuming that this has been adopted. So it will introduce a number of soft keywords. Consider what is proposed here to be the "perfect" syntax. Obviously in the absence of soft keywords, we would have to implement this a different way.
In short: don't fret over the syntax for the time being. We can iron that out later.
Oh, and many of the elements of the design are up for debate (like the reach of the various hierarchy inversions through other code). This document should be looked on as a start point and a rational, not the final word.
## Stateless Classes
A stateless class is a class that is declared with the following syntax:
std::stateless class ClassName : BaseClasses
{
...
};
The `std::stateless` soft keyword must be associated with any forward declarations for stateless types.
class ClassName; //Not a stateless type.
std::stateless class ClassName //Compiler error: you told me it wasn't stateless before.
{
};
And vice-versa.
A stateless class may not:
* Have any NSDMs that are not themselves stateless classes.
* Have any base classes that are not themselves stateless classes.
* Have any virtual member functions.
* Have any virtual base classes.
The size of a stateless type is not modified by being stateless. That is, you will get what you would normally expect from an ordinary empty class.
The standard library should have an appropriate template metamethod (and variable) for testing if a type is stateless.
Stateless classes only have a special effect when declared as subobjects of another class type. When a stateless class is an NSDM subobject, it has no effect on the size or layout of that type. A stateless NSDM does not alter the layout of the class of which it is a member. A stateless non-virtual base class does not alter the storage or layout of the derived class. Thus, the layout/size of a type is only affected by its non-stateless/virtual subobjects.
Stateless object members otherwise have the same effects on their containing types as regular types do. For example, a stateless class can cause its owners to not be trivially copyable by itself having a non-trivial copy constructor (even though the type by definition has nothing to copy).
Template types may be stateless as well. However, individual specializations are allowed to declare themselves to change their stateless status. So you could declare that a `tuple` would be stateless iff all of its members are stateless.
In all other ways (except where discussed below), stateless types can be used exactly like regular types.
### Implementation and Restrictions
In a perfect world, the above would be the only restrictions on stateless types. C++ of course is never perfect.
In C++ as it currently stands, every object needs to have a memory location. And two unrelated types cannot have the *same* memory location.
Stateless types effectively have to be able to break this rule. The memory location of a stateless member subobject can be the same location as the object it is a member of. And two sibling members of the same type may have the same location if one of them is stateless.
This is not really an implementation problem, since stateless classes by their very nature have no state to access. As such, the specific nature of their `this` pointer is irrelevant. So we simply need a strict aliasing rule that effectively allows stateless types to be able to have the same memory location as any other type (whether stateless or not). This should not cause much implementation headache, beyond having to change the layout rules to account for objects with no size.
A much bigger problem happens due to arrays. Or more specifically, pointer arithmetic on arrays.
In C++, the following should be perfectly valid for any type:
T t[5];
T *first = &t[0];
T *last = first + 5;
assert(sizeof(T) * 5 == last - first);
The whole point of a stateless type is that it does not take up space within another type. If the array above were a member of another type, this code should still work. But, since `sizeof(T)` is non-zero, that means that the member `t` takes up room in its containing type.
There are several ways to deal with this:
1. Declare that `sizeof` for stateless types is zero. I am not nearly familiar enough with the C++ standard to know the level of horrors that this would unleash, but I can imagine that it's very bad.
2. Declare that stateless types will only not take up space if they are not declared as arrays.
3. Forbid declaring arrays of stateless types altogether.
While #3 may seem a bit harsh, I think it is the best choice. For one, stateless types have no state, so declaring an array of them is pointless.
The downside of #3 is that it may impact template code, where the template does not know (or care) that a type is stateless. This would also be the first C++ construct that actively forbids such declarations on complete types. It would also forbid uses of stateless arrays in other contexts (stack variables, heap allocations), though primarily for orthogonality.
If the lack of orthogonality is too great, #2 can be adopted instead. The downside of #2 is that the statelessness of the class appears conditional: they don't take up room in other objects, except when they are arrayed. This also means that a stateless class must forbid NSDMs which are arrays of stateless classes.
Oh, and while the above definition only requires stateless classes to not take up space when used as subobjects, compilers are allowed to optimize their space usage in other cases too. While heap allocations must take up room, other allocations do not. So if a user declares a stateless member on the stack or as a global/namespace/member-static, the compiler is permitted to give the object no actual space.
## Inverted Class Members
Inverted classes of either kind can access their owning instance members from their member functions. But not from within *all* of their member functions.
Inverted class constructors and destructors explicitly do not have access to their owning class members. This is for the same reasons why virtual functions don't work in them. Member subobjects are constructed before their containing objects, so their owners don't exist yet. And member subobjects are destroyed after the destruction of their containing objects, so they would be accessing objects that have stopped existed.
But in all other cases, inverted class non-static member functions can access their owning class members. What access level they get is a question to be determined.
## Inverted Class Scoping
The current design allows inner classes and mixins to access *everything* from their containers, with such access propagating through inner classes and mixins up to the first non-inner class/mixin type. This includes private members.
This seems like it breaks encapsulation a lot. However, I would argue that it does not truly do so.
Inner classes can only be declared within another class declaration. They are therefore as much a part of that class as one of its member functions. Thus, they have every right to access the privates of their containing class.
Mixins are a bit more troublesome in theory. A mixin is not contained within a class definition. So giving it private access makes it seem like users can gain access to a type whenever they want.
However, the mixin syntax proposed below requires that the mixin is added to the class at declaration time. And therefore, the implementer of the class is the one who decides which mixins to use and which not to use. So users are unable to inject a mixin into a class that does not deliberately choose to use it. Using a mixin is thus like declaring that a class is a friend: the implementer is still the one in control of who gets access.
If this is considered a deal-breaker, we can restrict mixins to only being able to access the public members of those they are mixed into. Alternatively, we can add syntax to specify what a particular mixin has access to. The class deriving from the mixin would determine this.
## Inner Classes
An inner class is a nested class that is declared as follows:
std::inner class Name
{
...
};
Inner classes may only be declared as nested classes (they may be nested within another inner class). The class an inner class is declared within is the direct owning class of that inner class.
As with normal class definitions, `Name` is optional (anonymous inner classes can be are quite useful). The `inner` keyword is part of the class's declaration, so if it is forward declared, it must be consistently declared with it.
An inner class may or may not have the `stateless` keyword applied to it. If it does, they can go in either order.
An inner class can be derived from non-inner classes normally. An inner class may be derived from another inner class, but only if the *direct* owning class of both is the same, or the direct owning class of the derived inner class it itself derived (non-virtually) from the direct owning class of the base inner class.
Or to put it another way, from the derived inner class, it should be possible to go to its direct owner, then walk down the class hierarchy (non-virtually) to the class that is the direct owning class of the base inner class.
A non-inner class may not be derived from an inner class type.
Inner classes may have members and other definitions as befitting a regular class.
The standard library should have a template metafunction (and variable) for testing if a type is an inner class type.
### Type Usage
Variables of inner class types can only be declared as NSDM's of a class. And the class which they are declared within must be either:
* The direct owning class of the inner class.
* A class non-virtually inherited from the direct owning class of the inner class. The derived class in question should have access to the type (so private/protected inheritance can sever access, even without virtual inheritance).[^1]
Note that this means that the class which contains an instance of an inner class need not be exactly its owner. However, the container does need to be non-virtually derived from the owner.
Inner class types cannot be used as the type in `new` expressions. Objects of inner class types cannot have their destructors explicitly called. Temporaries cannot be created of inner class types.
Pointers and references to inner class types work as normal for any type.
Inner classes cannot be aggregated into arrays. The reason for this restriction [is discussed later](#inner_impl), but it is primarily due to implementation limitations.
How inner classes affect layout compatibility, trivial copyability, and various other aspects is very important. These decisions also factor heavily into how compilers can implement them. This section will list the decision that we propose to use; how it is possible to implement this will be [discussed later](#inner_impl). The specific interactions with the containing class are:
* Inner class members can be copied/moved exactly as any other type. Specifically, if a stateful inner class type is trivially copyable, it should be legal to do a `memcpy(dest, src, sizeof(T))` from it to another instance of that type, even if the destination instance is in a different containing object instance.
* The fact that a particular NSDM is of an inner class type has no effect on [trivial copyability](http://en.cppreference.com/w/cpp/concept/TriviallyCopyable) with regard to the containing class. So an inner class member could only prevent the containing class from being trivially copyable if the inner class itself were not trivially copyable.
* Types that declare stateful inner class members cannot have a trivial default constructor, and therefore cannot be [trivial types](http://en.cppreference.com/w/cpp/concept/TrivialType).
* Stateless inner class members do not affect the layout of the class containing them.
* Stateful inner class members prevent their owning types from being standard layout.
In all other ways, inner class types work just like regular types.
### Template Specialization
If an inner class is a template, then all of the specializations of that template must also be inner classes. And vice-versa. A template class's instantiation will therefore be either inner or not inner, regardless of the template parameters used on it.
The purpose of this rule is really user-sanity. Users probably don't mean to declare a specialization of some inner class template to not be an inner class. And vice-versa. Also, since the domain of inner class definitions is limited to within some other class, the potential for someone injecting such a definition is low. So it's far more likely that this would happen due to user error than deliberate action.
### Containment Scope
Inner classes are implicitly friends of their owning class. If the owning class is itself an inner class, this friendship propagates up the ownership hierarchy to the first non-inner class type.
Within member functions of inner classes (except for constructors and destructors), the following rules are added.
Members (static or not) of an inner class may access members of its owning instance through a pointer/reference to its own class (including implicit `this`) as though it were derived from its owning class type. Name lookup takes place as though the owning class were placed at the end of the inner class's list of derived classes. Therefore explicitly derived classes take priority over owning ones. This implied derivation propagates up the ownership hierarchy through inner classes until the first non-inner class is reached.
Qualified lookup within inner class member functions can also be performed up the ownership hierarchy as well. Similarly, inner classes may implicitly convert pointers/references to their own types into pointers/references to their owners, up the hierarchy until the first non-inner class is reached.
Note that the above properties are also conferred onto any lambda functions created by such members. Friends of inner classes may gain these abilities as well (mainly to allow lambdas to work). Non-friends of inner classes cannot do these.
Non-friends may perform explicit conversions from a pointer/reference to an inner class to a pointer/reference to one of its owning classes via `static_cast`. But implicit conversion is not allowed.
### Implementation {#inner_impl}
The fundamental operation of inner classes is the (static) transformation from a pointer to the inner class type to a pointer to its direct owning class type. The implementation of this operation has to be done separately for two cases: one for stateless inner classes and one for non-stateless inner classes.
The stateless version is based primarily on the fact that pointers to stateless classes could point to anything. Including... directly at their owning class instance. So the memory location of a stateless pointer should always be the same memory location of their owning class. If their owning class is itself a stateless inner class, then this propagates up the ownership hierarchy to the first non-stateless inner class or non-inner class.
Note that if the owner is a stateless non-inner class, then the memory location for him should be whatever the compiler deems convenient.
There is a snag that appears when a stateless inner class is a member of a derived class. In this case, the compiler needs to recognize that the owning class is a base class of its container. This is a static property, so it is easy to know about. Once the compiler recognizes that, it can assign the pointer/reference by doing a `static_cast` to the base class, then converting that memory location to the inner class type.
This becomes more complex with the following:
class Base
{
public:
inner stateless struct In1
{ int GetBase() const {return baseVar;} };
private:
int baseVar;
};
class Derived : public Base;
{
public:
inner stateless struct In2 : public Base::In1
{
int GetBase2() const {return GetBase();}
};
In2 acc;
private:
int deriVar;
};
The key to remember here is that both `Base` and `Derived` have non-stateless NSDMs, so they both take up space. `Derived` is not standard layout, and therefore the pointer to `Base` need not have the same memory location as a pointer to derived`Derived`.
Even with this complexity, things still work. For `Derived::In2::GetBase2`, the compiler recognizes that `GetBase` is in the base class of `Derived::In2`. But it also realizes that this base class is an inner class, who's owner is a base class of this inner class's owner.
So it converts the current `this` pointer of type `Derived::In2*` into a `Derived*` (changing only the type), then converts it into a `Base*` (possibly changing the memory location), then converts it into an `Base::In1*` (again changing only the type). The same thing happens for calling `acc.GetBase()` directly.
All of these conversions are static, based on compile-time offsets. They do not rely on a particular instance of any of the stateless inner classes.
Stateful inner classes are more complex. This is because each stateful class instance must have a separate address from every other stateful NSDM. Because each member has its own state, the conversion from `Outer::Inner*` to `Outer*` is no longer a simple typecast or a static pointer offset. And if we want to be able to pass around references to stateful inner classes, we need to be able to, from the inner class's `this` pointer alone, convert it into a pointer to the owning class.
Therefore, for each stateful inner class member, the compiler must have access to some data (typically a byte offset) which is used to convert `this` from `Outer::Inner*` to `Outer*`. For multiple nestings of stateful inner classes, each level has its own offset to get to the next level.
The question is where this offset is stored.
One thing is certain: the offset must be stored someplace that is accessible from just a pointer to `this`. After all, users may have only a pointer/reference to an inner class member, and they have every reason to expect that this will be a working pointer/reference. In such a case, the only pieces of information the compiler has are the class definitions and `this`.
We are left with two alternative implementations. The offset could be stored within the inner class itself, in a hidden member (ala vtable pointers). Or the offset could be stored in the direct owning class, in memory that is directly adjacent to the instance.
Both of these have behavioral consequences. Both solutions break standard layout, as a consequence of having to insert additional data. But this is to be expected. The question is what functionality each option allows and what it forbids.
**Option 1: In-Object Memory:** This is the most obvious implementation strategy. The offset is simply a hidden field of the inner class.
The downside of this approach is that stateful inner classes *cannot* be trivially copied. This is for the same reason that classes involving virtuals cannot be trivially copied. The value of the offset is based on the specific member variable and where it is within its owning type. As such, copying it between two *different* members (of the same type) is really bad. And since the offset is part of the type, you can't copy the two types with a simple `memcpy(dest, src, sizeof(T))`. And therefore, they are not trivially copyable.
The odd part here is that, while inner classes themselves are not trivially copyable, the class containing inner class members still can be trivially copied. This works because the data is an offset, not a pointer directly to the owner. As such, trivial copies can work if they are they are between the same variable (the variable defines the offset). Which they will be if the copy is between to instances of the containing class.
So this option retains trivial copyability for the container, at the expense of trivial copyability for the members. And it makes the rules more complex.
Also, the containing class cannot be *trivial*. This is because the stateful inner class members are also not trivial; their default copy constructors must initialize this hidden data. And the container must provide them with the hidden offset to initialize it with.
**Option 2: Adjacent Memory:** The offset could be stored in the containing class as a hidden value. The offset would be stored directly before its associated inner class.
If the offset is adjacent to the object, then fetching the offset with only `this` is easy: simply increment/decrement the pointer so that it points to the adjacent memory. The class definition will tell the compiler if it is stateful, and if it is, then it knows it needs to do this work to get the offset.
The adjacent memory method preserves trivial copyability of both the inner and containing classes, because the offset is where it needs to be: associated with the type that actually defines that offset. The offset is defined by the arrangement of the members of the outer class. So the outer class is the one that stores the offset. The outer class can still be trivially copied because the offset for each member is a static property, not a dynamic one.
The outer class cannot be *trivial* however, since its default constructor (and every other non-trivial-copy/move constructor) will need to initialize this hidden data.
The downside of this choice is that it breaks the ability to have arrays of stateful inner class members. The reason being that arrays in C++ are required to be contiguous, that the pointer distance from one array element to another must be exactly `sizeof(Class)`.
Note that if we forbid arrays of stateless classes period, forbidding arrays of state<b>ful</b> inner classes is not far afield.
The question is which is more useful: simpler trivial copyability rules and trivial copyability for inner classes vs. aggregating arrays of stateful inner classes? This proposal focuses on the former.
### Other Implementation Based Restrictions
You will notice quite a lot of places where virtual inheritance is forbidden. This is done to ensure that all of the pointer conversions remain *static* operations. Virtual inheritance would require dynamic operations, where a simple pointer offset is not possible.
Strict aliasing in C++ says that two unrelated pointers point to different objects. However, inner class members are related to one another, even if they are technically siblings. So the definition of "related" needs to be expanded to cover inner classes.
Specifically, inner class types are related to the class they are declared within. Recursively up to the first non-inner class.
You may also notice that this design explicitly does not allow the more open Java syntax. That is, there is no way for arbitrary code to dynamically allocate inner classes to some object. This is primarily to reduce the implementation burden. We would need to not only add entirely new syntax for it, we would need to allow binding of inner classes to smart pointers to their owning types or similar constructs.
## Mixin Classes
The syntax for mixin declarations is somewhat in question at present. The general ideas for the syntax is to:
1. Make being a mixin a first-class property of the type, which is associated wtih the type's declaration. This makes it possible for the compiler to verify that the user is using the mixin type correctly.
2. Rather than arbitrarily deciding which template parameter is the derived class type, allow the user to specify it explicitly. This makes the system easier for users to use, allowing the template argument interface to be what is most natural for the type.
3. Allow the directly derived class to not have to type its classname directly into the base class instantiation. This prevents user error, as well as making it abundantly clear to the reader that a mixin is being used.
4. Prevent users from accidentally putting the derived class type in the wrong location in the argument list. That is, both the derived class and the mixin implementation must agree which template argument is the mixin's owning type.
### Class Declaration
A mixin class is a template class that is declared as follows:
template<parameters>
class name std::mixin<parameter>
{
...
};
The `parameter` associated with the `mixin` keyword must be the name of one of the `parameters` listed in the template argument list. This parameter must be a typename (it can be constrained by a concept). The specified template parameter becomes the owning type for the mixin.
The `parameter` may not be any form of expression; it must be exactly the name of a template parameter.
The `mixin` part of the class declaration must be a part of any forward declarations of the class.
Mixin classes obey all the rules of templates and classes, except where noted below.
The standard library should have a template metafunction (and variable) for testing if a type is an mixin class.
### Type Usage
Mixin template classes can be explicitly instantiated, but no variables of their types can be declared. Objects of mixin types can only be created as base subobjects of other classes. Virtual inheritance of mixin classes is not allowed. Special syntax must be used when deriving from a mixin class as well:
template<typename Mixin
class name std::mixin<Mixin>
{
...
};
class derived : public name<std::mixin>
{
};
Here, the `mixin` keyword is used in the template instantiation syntax to refer to the specific template parameter that was specified to be the mixin type. The template will be instantiated as if `mixin` was replaced by `derived`. If the mixin has multiple template parameters, the others can be filled in as normal. The `mixin` keyword must be applied to the template parameter that was declared to be the mixin parameter in the template declaration.
Template substitution works exactly as if the user had used `name<derived>`.
Not using the `mixin` keyword with a mixin class template is an error, even if it correctly names the derived type:
class mine : public name<mine> //Not allowed
{
};
Similarly, when deriving from non-mixin types, the use of the `mixin` keyword in the template argument list is not allowed. Also, if the `mixin` keyword is used in place of a parameter that is not the mixin parameter, a compiler error results.
When naming the concrete mixin base class *within* the definition of members of the direct derived class, `mixin` must be used in the template argument list. Outside of members of the derived class, the user must name the derived class explicitly.
Pointers and references to mixin classes can be used as normal. Mixin base classes operate as normal base classes do, except where mentioned below.
### Type Aliases and Mixins {#mixin_aliases}
Aliases for mixins work unusually. A mixin must always have a template parameter specified as the mixin's owning type. And aliases are no exception. Therefore, `typedef` cannot be used to create a mixin alias, only `using` syntax.
The `using` declaration must have at least one template parameter, and the syntax must specify which template parameter is the mixin's owning type. Furthermore, that parameter must be used in the alias itself. Here is an example:
template<typename T, typename derived>
class old_mixin std::mixin<derived>;
typedef old_mixin<int, some_class> bad1; //Illegal, no template parameter for the mixin type.
template<typename T>
using bad2 = old_mixin<T, some_class>; //Illegal, mixin type must refer to a template parameter.
template<typename derived>
using bad3 = old_mixin<int, derived>; //Illegal, new alias must specify which parameter is a mixin.
template<typename T, typename derived>
using bad4 std::mixin<T> = old_mixin<T, derived>; //Illegal, the specified mixin parameter is not passed to the original type's mixin parameter.
template<typename derived>
using correct std::mixin<derived> = old_mixin<int, derived>; //Works
Mixin template parameters can be constrained by concepts, but they must still have a type parameter.
The reason why mixin aliases must always have a template parameter for the mixin type is because of the syntax used for deriving from a mixin. The current syntax has the user put the `mixin` keyword in the template argument list, which not only fills in the class but tells the compiler to make sure it's a mixin.
If we want to have cases like `bad1` or `bad2` work (those mixins could only be used with `some_class`), it is still important that the user deriving from the class make it clear that they are aware that it is a mixin. Because inheriting from a mixin [implicitly makes the mixin a friend of the class](#mixin_scope), it is very important to make it clear to the user at all times that a mixin is being derived from.
### Mixin Specializations
If one specialization of a template class is not a mixin, then all specializations must not be mixins.
Similarly, if one specialization is a mixin, all of them must be mixins. A mixin specialization may not declare a concrete class to be the mixin type. The parameter may be constrained via concepts, but it must always be a template parameter.
The reason for this limitation is similar to the [one for aliases](#mixin_aliases). If we allow specializations for a specific mixin base/derived class pairing, there would need to be some way for the user of the mixin to provide some idea that they intend to derive from that mixin.
### Inversion Scope {#mixin_scope}
Scoping for mixins is a bit more complex, due to the fact that inheritance access classes (public/private/protected) scope how far a mixin can reach.
A mixin instantiation within a class hierarchy has reach through every mixin that derives from it, up until one of the following is reached:
* A non-mixin class
* A mixin class that used private inheritance on this branch of the tree
All classes between the mixin class and this class (including that class) represent the "scope" of the mixin.
Every class within the mixin's scope is implicitly friends with the mixin.
Within member functions of mixin classes (except for constructors and destructors), the following rules are added.
Members (static or not) of an mixin class may access members of any instance of its owning instance through a pointer/reference to its own class (including implicit `this`) as though it were derived from that class. Name lookup takes place as though the owning class were placed at the end of the mixin class's list of derived classes. Therefore explicitly derived classes take priority over owning ones. Name lookup propagates up through the entirety of the mixin's scope.
Qualified lookup within mixin member functions can also be performed up the ownership hierarchy as well. Similarly, mixin classes may implicitly convert pointers/references to their own types into pointers/references to their owners, up through the mixin's scope.
Note that the above properties are also conferred onto any lambda functions created by such members. Friends of mixin classes may gain these abilities as well (mainly to allow lambdas to work). Non-friends of mixin classes cannot do these.
The ability of non-friends to perform such conversions depends entirely on the access classes of those non-friends and of the inheritance diagram. Or to put it another way, the rules of casting up and down the hierarchy remain unchanged for non-friends of mixins.
### Possible Additions
* Given a mixin class, syntax could be added to get the derived class type. Perhaps `mixin<mixin_type>` would resolve to a typename.
### Mixing Mixins and Inner Class
Inner classes and mixins propagate access and friendship up to the first non-inner class or non-mixin (or the first private-inheritance mixin). But because they are really the same concept implemented in different ways, they should interact well together.
Therefore, if an inner class derives from a mixin non-privately, the mixin's scope will be extended through the inner class. And if a mixin declares an inner class, the same applies: that inner class's scope extends through the mixin.
So if you have this:
template<typename derived>
struct M2 mixin<derived>
{
inner struct I : M1<mixin>
{
int val_i;
} in;
int val_m2
}
struct T : public M2<mixin>
{
int val_t;
};
In instances of `T`, functions from `M2<T>::M1<I>` will be able to access `val_i`, `val_m2`, and `val_t`, in that order.
This feature allows users to use mixins to write the implementation of inner classes outside of the containing class. Thus the guts of an implementation can be used in multiple classes, while still allowing tclass-specific tweaks to be provided by the inner class using the mixin. For example, `I` could provide its own `val_m2` that shadows what `M1` can access. And because `M1`'s implementation is ignorant about the world outside of the mixin that it is declared within, it has no way to bypass this shadowing.
# Remaining Issues to Resolve
* Arrays of stateless types: allowed or forbidden?
* Stateful inner class trivial copyability vs. declaring arrays of inner classes.
* How inner classes work with pointers-to-members.
* Mixin aliases & specializations. Currently, the mixin owning type must always be a template parameter, preventing complete mixin specializations for specific types. Should we allow full specialization?
* The greed of scoping rules. For example, consider the [suggested property implementation](#uses_properties) previously stated. The property mixin would have access to the inner class, but it would also be able to access the owner of the inner class. Which means that it could accidentally mistake one of the owner's members for that of the inner class.
For inner classes, arbitrary reach makes sense, as they are effectively pieces of some other class. But for mixins, there probably should be some way to explicitly declare that their scope ends with the class that uses them, even if it is an inner class. Without using `private` inheritance, that is, as this would prevent the derived class from making its interface public. This could be done at derivation time, with a parameter passed to the `mixin` keyword: `class derived : public mix<mixin: public>` or something of the like.
[^1]: Implementation note. For stateless inner classes in derived classes, the pointer to the stateless class has to have the same value as the base class. So special pointer gymnastics are needed when getting pointers/references to such variables. For stateful inner classes, the offset simply needs to be able to jump out of the derived class type and into the base class.
------=_Part_10243_2020340158.1446141886863--
.
Author: Tony V E <tvaneerd@gmail.com>
Date: Thu, 29 Oct 2015 14:31:44 -0400
Raw View
--001a11c377a2e59c1d0523428585
Content-Type: text/plain; charset=UTF-8
Have you looked at mixins from other languages, such as D? (For better or
worse. ie for both what works well and what we should avoid.)
Tony
On Thu, Oct 29, 2015 at 2:04 PM, Nicol Bolas <jmckesson@gmail.com> wrote:
> A while back, we had the semi-annual "let's put properties in C++
> discussion
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/n0QcD_K5dBY/3ZW3MpJrmhIJ>".
> Unlike most such discussions however, that led to a more generalized idea: adding
> with a restrictive form of Java-style inner classes
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/Yorc58iiBwAJ>,
> which could be used to implement properties sanely. Several more concrete
> ideas
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/G5MsctCECAAJ>
> were put forth along these lines, including one of my own that introduced
> a separate idea for stateless types
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/gxAxe5xBAQAJ>
> .
>
> However, that idea hit a large snag, in that inner classes have to be
> nested in the classes they use. So if you're trying to implement something
> complex like properties, you'd have to add tons of boilerplate code to make
> the inner class proxy work. For every property you write. That's not to say
> that inner classes are all about properties, but that is a use case of them.
>
> The original inline class idea had the notion of being able to declare
> such constructs outside of a class, but they had some limitations. Then, I
> started playing around with the idea of allowing "inner classes" to be
> declared outside of a type, but as a template. One of the template
> parameters would be filled in with the actual class it is an inner class
> of. So the common boilerplate could go into there.
>
> And then, I realized that I was really just solving the CRTP problem. That
> is, I was declaring a way to write a class that would be used by another
> class, injecting its definitions into the other class as if they were one
> type.
>
> In short, I'd created language support for C++ mixins. Indeed, I realized
> that inner classes and mixins are really kinda the same thing, if you think
> about their effects.
>
> So... here's a proposal that provides mixins, inner classes, and stateless
> classes to C++. What do you think? Is this a functional idea? What cases
> did I miss?
>
> --
>
> ---
> 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.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--001a11c377a2e59c1d0523428585
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div>Have you looked at mixins from other languages, such =
as D?=C2=A0 (For better or worse.=C2=A0 ie for both what works well and wha=
t we should avoid.)<br></div>Tony<br></div><div class=3D"gmail_extra"><br><=
div class=3D"gmail_quote">On Thu, Oct 29, 2015 at 2:04 PM, Nicol Bolas <spa=
n dir=3D"ltr"><<a href=3D"mailto:jmckesson@gmail.com" target=3D"_blank">=
jmckesson@gmail.com</a>></span> wrote:<br><blockquote class=3D"gmail_quo=
te" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"=
><div dir=3D"ltr">A while back, we had the semi-annual "<a href=3D"htt=
ps://groups.google.com/a/isocpp.org/d/msg/std-proposals/n0QcD_K5dBY/3ZW3MpJ=
rmhIJ" target=3D"_blank">let's put properties in C++ discussion</a>&quo=
t;. Unlike most such discussions however, that led to a more generalized id=
ea: <a href=3D"https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u=
35GIuJECcQ/Yorc58iiBwAJ" target=3D"_blank">adding with a restrictive form o=
f Java-style inner classes</a>, which could be used to implement properties=
sanely. Several <a href=3D"https://groups.google.com/a/isocpp.org/d/msg/st=
d-proposals/u35GIuJECcQ/G5MsctCECAAJ" target=3D"_blank">more concrete ideas=
</a> were put forth along these lines, including one of my own that <a href=
=3D"https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/=
gxAxe5xBAQAJ" target=3D"_blank">introduced a separate idea for stateless ty=
pes</a>.<br><br>However, that idea hit a large snag, in that inner classes =
have to be nested in the classes they use. So if you're trying to imple=
ment something complex like properties, you'd have to add tons of boile=
rplate code to make the inner class proxy work. For every property you writ=
e. That's not to say that inner classes are all about properties, but t=
hat is a use case of them.<br><br>The original inline class idea had the no=
tion of being able to declare such constructs outside of a class, but they =
had some limitations. Then, I started playing around with the idea of allow=
ing "inner classes" to be declared outside of a type, but as a te=
mplate. One of the template parameters would be filled in with the actual c=
lass it is an inner class of. So the common boilerplate could go into there=
..<br><br>And then, I realized that I was really just solving the CRTP probl=
em. That is, I was declaring a way to write a class that would be used by a=
nother class, injecting its definitions into the other class as if they wer=
e one type.<br><br>In short, I'd created language support for C++ mixin=
s. Indeed, I realized that inner classes and mixins are really kinda the sa=
me thing, if you think about their effects.<br><br>So... here's a propo=
sal that provides mixins, inner classes, and stateless classes to C++. What=
do you think? Is this a functional idea? What cases did I miss?<span class=
=3D"HOEnZb"><font color=3D"#888888"><br></font></span></div><span class=3D"=
HOEnZb"><font color=3D"#888888">
<p></p>
-- <br>
<br>
--- <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" target=3D"_=
blank">std-proposals+unsubscribe@isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org" target=3D"_blank">std-proposals@isocpp.org</a>.<br>
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/" target=3D"_blank">http://groups.google.com/a/isocpp.org/gro=
up/std-proposals/</a>.<br>
</font></span></blockquote></div><br></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a11c377a2e59c1d0523428585--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 29 Oct 2015 12:14:09 -0700 (PDT)
Raw View
------=_Part_10003_1233196788.1446146049155
Content-Type: multipart/alternative;
boundary="----=_Part_10004_960457653.1446146049155"
------=_Part_10004_960457653.1446146049155
Content-Type: text/plain; charset=UTF-8
On Thursday, October 29, 2015 at 2:31:46 PM UTC-4, Tony V E wrote:
>
> Have you looked at mixins from other languages, such as D? (For better or
> worse. ie for both what works well and what we should avoid.)
>
I didn't look at it while crafting the proposal.
Taking a quick look at D <http://dlang.org/template-mixin.html>, it treats
(template) mixins as a first-class construct, distinct from others. That is
far more of a heavy-handed approach than I wanted to get into here. It's
basically a form of safe macro expansion. What I proposed is just a class,
with a few restrictions and automatic helpers on its use. C++'s multiple
inheritance functionality is leveraged to do the actual injection.
D's version may be better, in that the declarations becomes truly a part of
the class. But it would be a *lot* more complicated of a proposal than just
a few adjustments to certain uses of classes.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_10004_960457653.1446146049155
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Thursday, October 29, 2015 at 2:31:46 PM UTC-4, Tony V =
E 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>=
Have you looked at mixins from other languages, such as D?=C2=A0 (For bette=
r or worse.=C2=A0 ie for both what works well and what we should avoid.)<br=
></div></div></blockquote><div><br>I didn't look at it while crafting t=
he proposal.<br><br><a href=3D"http://dlang.org/template-mixin.html">Taking=
a quick look at D</a>, it treats (template) mixins as a first-class constr=
uct, distinct from others. That is far more of a heavy-handed approach than=
I wanted to get into here. It's basically a form of safe macro expansi=
on. What I proposed is just a class, with a few restrictions and automatic =
helpers on its use. C++'s multiple inheritance functionality is leverag=
ed to do the actual injection.<br><br>D's version may be better, in tha=
t the declarations becomes truly a part of the class. But it would be a <i>=
lot</i> more complicated of a proposal than just a few adjustments to certa=
in uses of classes.<br></div></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_10004_960457653.1446146049155--
------=_Part_10003_1233196788.1446146049155--
.
Author: =?UTF-8?Q?Klaim_=2D_Jo=C3=ABl_Lamotte?= <mjklaim@gmail.com>
Date: Fri, 30 Oct 2015 00:59:33 +0100
Raw View
--001a11c240d04449310523471a06
Content-Type: text/plain; charset=UTF-8
The word "mixin" means different things in different languages.
The current discussion is confusing because of this.
I think that what you want is "interface composition" which is not what D
calls a mixin.
On 29 October 2015 at 20:14, Nicol Bolas <jmckesson@gmail.com> wrote:
> On Thursday, October 29, 2015 at 2:31:46 PM UTC-4, Tony V E wrote:
>>
>> Have you looked at mixins from other languages, such as D? (For better
>> or worse. ie for both what works well and what we should avoid.)
>>
>
> I didn't look at it while crafting the proposal.
>
> Taking a quick look at D <http://dlang.org/template-mixin.html>, it
> treats (template) mixins as a first-class construct, distinct from others.
> That is far more of a heavy-handed approach than I wanted to get into here.
> It's basically a form of safe macro expansion. What I proposed is just a
> class, with a few restrictions and automatic helpers on its use. C++'s
> multiple inheritance functionality is leveraged to do the actual injection.
>
> D's version may be better, in that the declarations becomes truly a part
> of the class. But it would be a *lot* more complicated of a proposal than
> just a few adjustments to certain uses of classes.
>
> --
>
> ---
> 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.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--001a11c240d04449310523471a06
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">The word "mixin" means different things in diffe=
rent languages.<div>The current discussion is confusing because of this.</d=
iv><div>I think that what you want is "interface composition" whi=
ch is not what D calls a mixin.<br></div><div><br></div></div><div class=3D=
"gmail_extra"><br><div class=3D"gmail_quote">On 29 October 2015 at 20:14, N=
icol Bolas <span dir=3D"ltr"><<a href=3D"mailto:jmckesson@gmail.com" tar=
get=3D"_blank">jmckesson@gmail.com</a>></span> wrote:<br><blockquote cla=
ss=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;pa=
dding-left:1ex"><div dir=3D"ltr"><span class=3D"">On Thursday, October 29, =
2015 at 2:31:46 PM UTC-4, Tony V E 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>Have you looked at mixins from other languages,=
such as D?=C2=A0 (For better or worse.=C2=A0 ie for both what works well a=
nd what we should avoid.)<br></div></div></blockquote></span><div><br>I did=
n't look at it while crafting the proposal.<br><br><a href=3D"http://dl=
ang.org/template-mixin.html" target=3D"_blank">Taking a quick look at D</a>=
, it treats (template) mixins as a first-class construct, distinct from oth=
ers. That is far more of a heavy-handed approach than I wanted to get into =
here. It's basically a form of safe macro expansion. What I proposed is=
just a class, with a few restrictions and automatic helpers on its use. C+=
+'s multiple inheritance functionality is leveraged to do the actual in=
jection.<br><br>D's version may be better, in that the declarations bec=
omes truly a part of the class. But it would be a <i>lot</i> more complicat=
ed of a proposal than just a few adjustments to certain uses of classes.<br=
></div></div><div class=3D"HOEnZb"><div class=3D"h5">
<p></p>
-- <br>
<br>
--- <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" target=3D"_=
blank">std-proposals+unsubscribe@isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org" target=3D"_blank">std-proposals@isocpp.org</a>.<br>
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/" target=3D"_blank">http://groups.google.com/a/isocpp.org/gro=
up/std-proposals/</a>.<br>
</div></div></blockquote></div><br></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a11c240d04449310523471a06--
.
Author: "'Matt Calabrese' via ISO C++ Standard - Future Proposals" <std-proposals@isocpp.org>
Date: Thu, 29 Oct 2015 17:14:36 -0700
Raw View
--001a113de976185a180523475037
Content-Type: text/plain; charset=UTF-8
On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <jmckesson@gmail.com> wrote:
>
> And then, I realized that I was really just solving the CRTP problem. That
> is, I was declaring a way to write a class that would be used by another
> class, injecting its definitions into the other class as if they were one
> type.
>
> In short, I'd created language support for C++ mixins. Indeed, I realized
> that inner classes and mixins are really kinda the same thing, if you think
> about their effects.
>
> So... here's a proposal that provides mixins, inner classes, and stateless
> classes to C++. What do you think? Is this a functional idea? What cases
> did I miss?
>
I'm just starting to skim this, but it would help a lot if this started off
with motivating cases that lead to the desire for such facilities. It sort
of jumps right into definitions and implementation without first explaining
what issues the proposal helps with. Benefits of mixins in some form might
seem obvious to some, but it should probably be a little more formally
presented up top.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--001a113de976185a180523475037
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div class=3D"gmail_extra"><div class=3D"gmail_quote">On T=
hu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <span dir=3D"ltr"><<a href=3D"=
mailto:jmckesson@gmail.com" target=3D"_blank">jmckesson@gmail.com</a>></=
span> wrote:<blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;bo=
rder-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr">And then, I rea=
lized that I was really just solving the CRTP problem. That is, I was decla=
ring a way to write a class that would be used by another class, injecting =
its definitions into the other class as if they were one type.<br><br>In sh=
ort, I'd created language support for C++ mixins. Indeed, I realized th=
at inner classes and mixins are really kinda the same thing, if you think a=
bout their effects.<br><br>So... here's a proposal that provides mixins=
, inner classes, and stateless classes to C++. What do you think? Is this a=
functional idea? What cases did I miss?</div></blockquote><div><br></div><=
div>I'm just starting to skim this, but it would help a lot if this sta=
rted off with motivating cases that lead to the desire for such facilities.=
It sort of jumps right into definitions and implementation without first e=
xplaining what issues the proposal helps with. Benefits of mixins in some f=
orm might seem obvious to some, but it should probably be a little more for=
mally presented up top.</div></div></div></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a113de976185a180523475037--
.
Author: "'Matt Calabrese' via ISO C++ Standard - Future Proposals" <std-proposals@isocpp.org>
Date: Thu, 29 Oct 2015 17:36:41 -0700
Raw View
--001a1134fb0a05175d0523479f1a
Content-Type: text/plain; charset=UTF-8
On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <jmckesson@gmail.com> wrote:
>
> So... here's a proposal that provides mixins, inner classes, and stateless
> classes to C++. What do you think? Is this a functional idea? What cases
> did I miss?
>
The analysis accompanying the example of CRTP composition that is provided
is a bit misleading. You can compose such constructs with CRTP fairly
easily in practice and the metaphor comparing it to pure virtual functions
doesn't even break down -- all you do is you pass in the derived type in
the middle base as opposed to middle base's instantiation. In other words,
in your example, you'd do ": public A<derived>" rather than ": public
A<B<derived>>" if the base needs to depend on facilities provided in
derived. Maybe there is some other kind of point you are trying to make
with the code example there, but it's not immediately clear to me. This
kind of composition is not too uncommon with CRTP.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--001a1134fb0a05175d0523479f1a
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div class=3D"gmail_extra"><div class=3D"gmail_quote">On T=
hu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <span dir=3D"ltr"><<a href=3D"=
mailto:jmckesson@gmail.com" target=3D"_blank">jmckesson@gmail.com</a>></=
span> wrote:<blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0=
..8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-s=
tyle:solid;padding-left:1ex"><div dir=3D"ltr">So... here's a proposal t=
hat provides mixins, inner classes, and stateless classes to C++. What do y=
ou think? Is this a functional idea? What cases did I miss?</div></blockquo=
te></div><br></div><div class=3D"gmail_extra">The analysis accompanying the=
example of CRTP composition that is provided is a bit misleading. You can =
compose such constructs with CRTP fairly easily in practice and the metapho=
r comparing it to pure virtual functions doesn't even break down -- all=
you do is you pass in the derived type in the middle base as opposed to mi=
ddle base's instantiation. In other words, in your example, you'd d=
o ":=C2=A0<span style=3D"color:rgb(0,0,0)">public A<derived>&quo=
t; rather than ":=C2=A0</span><span style=3D"color:rgb(0,0,0)">public =
A<B<derived>>" if the base needs to depend on facilities p=
rovided in derived. Maybe there is some other kind of point you are trying =
to make with the code example there, but it's not immediately clear to =
me. This kind of composition is not too uncommon with CRTP.</span></div></d=
iv>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a1134fb0a05175d0523479f1a--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 29 Oct 2015 17:45:51 -0700 (PDT)
Raw View
------=_Part_79_1772150466.1446165951722
Content-Type: multipart/alternative;
boundary="----=_Part_80_1037676507.1446165951722"
------=_Part_80_1037676507.1446165951722
Content-Type: text/plain; charset=UTF-8
On Thursday, October 29, 2015 at 8:36:44 PM UTC-4, Matt Calabrese wrote:
>
> On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <jmck...@gmail.com
> <javascript:>> wrote:
>>
>> So... here's a proposal that provides mixins, inner classes, and
>> stateless classes to C++. What do you think? Is this a functional idea?
>> What cases did I miss?
>>
>
> The analysis accompanying the example of CRTP composition that is provided
> is a bit misleading. You can compose such constructs with CRTP fairly
> easily in practice and the metaphor comparing it to pure virtual functions
> doesn't even break down -- all you do is you pass in the derived type in
> the middle base as opposed to middle base's instantiation. In other words,
> in your example, you'd do ": public A<derived>" rather than ": public
> A<B<derived>>" if the base needs to depend on facilities provided in
> derived.
>
And what if you want half in half? That is, if B should provide some of the
facilities that A requires, while `derived` provides the rest?
As a more concrete case, let's say that mixin `A` requires that the derived
class implement `foo` and `bar`. Now `B` uses the mixin, and it implements
`bar`, while requiring that it's user implement `blah`. Therefore, the
combined mixin `B` requires `foo` and `blah`. So `derived` implements `foo`
and `blah`.
That can't happen with the CRTP. One class must implement the full
requirements. While it may be `derived` rather than `B`, there is no chance
for one to implement some and one to implement the other.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_80_1037676507.1446165951722
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Thursday, October 29, 2015 at 8:36:44 PM UTC-4, Matt Calabrese w=
rote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8e=
x;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><di=
v class=3D"gmail_quote">On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <span=
dir=3D"ltr"><<a href=3D"javascript:" target=3D"_blank" gdf-obfuscated-m=
ailto=3D"__ftA5tUEwAJ" rel=3D"nofollow" onmousedown=3D"this.href=3D'jav=
ascript:';return true;" onclick=3D"this.href=3D'javascript:';re=
turn true;">jmck...@gmail.com</a>></span> wrote:<blockquote class=3D"gma=
il_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-le=
ft-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div di=
r=3D"ltr">So... here's a proposal that provides mixins, inner classes, =
and stateless classes to C++. What do you think? Is this a functional idea?=
What cases did I miss?</div></blockquote></div><br></div><div>The analysis=
accompanying the example of CRTP composition that is provided is a bit mis=
leading. You can compose such constructs with CRTP fairly easily in practic=
e and the metaphor comparing it to pure virtual functions doesn't even =
break down -- all you do is you pass in the derived type in the middle base=
as opposed to middle base's instantiation. In other words, in your exa=
mple, you'd do ":=C2=A0<span style=3D"color:rgb(0,0,0)">public A&l=
t;derived>" rather than ":=C2=A0</span><span style=3D"color:rg=
b(0,0,0)">public A<B<derived>>" if the base needs to depen=
d on facilities provided in derived.</span></div></div></blockquote><div><b=
r>And what if you want half in half? That is, if B should provide some of t=
he facilities that A requires, while `derived` provides the rest?<br><br>As=
a more concrete case, let's say that mixin `A` requires that the deriv=
ed class implement `foo` and `bar`. Now `B` uses the mixin, and it implemen=
ts `bar`, while requiring that it's user implement `blah`. Therefore, t=
he combined mixin `B` requires `foo` and `blah`. So `derived` implements `f=
oo` and `blah`.<br><br>That can't happen with the CRTP. One class must =
implement the full requirements. While it may be `derived` rather than `B`,=
there is no chance for one to implement some and one to implement the othe=
r.</div><br>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_80_1037676507.1446165951722--
------=_Part_79_1772150466.1446165951722--
.
Author: "'Matt Calabrese' via ISO C++ Standard - Future Proposals" <std-proposals@isocpp.org>
Date: Thu, 29 Oct 2015 17:47:39 -0700
Raw View
--001a113685e245ce1d052347c617
Content-Type: text/plain; charset=UTF-8
On Thu, Oct 29, 2015 at 5:36 PM, Matt Calabrese <calabrese@google.com>
wrote:
> On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <jmckesson@gmail.com> wrote:
>>
>> So... here's a proposal that provides mixins, inner classes, and
>> stateless classes to C++. What do you think? Is this a functional idea?
>> What cases did I miss?
>>
>
> The analysis accompanying the example of CRTP composition that is provided
> is a bit misleading. You can compose such constructs with CRTP fairly
> easily in practice and the metaphor comparing it to pure virtual functions
> doesn't even break down -- all you do is you pass in the derived type in
> the middle base as opposed to middle base's instantiation. In other words,
> in your example, you'd do ": public A<derived>" rather than ": public
> A<B<derived>>" if the base needs to depend on facilities provided in
> derived. Maybe there is some other kind of point you are trying to make
> with the code example there, but it's not immediately clear to me. This
> kind of composition is not too uncommon with CRTP.
>
Just some advice -- I wouldn't depend on soft keywords as you are. Unless
I'm misremembering, at Kona it ultimately wasn't even presented by Gor and
I get the sense that there is little chance it would be accepted.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--001a113685e245ce1d052347c617
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div class=3D"gmail_extra"><div class=3D"gmail_quote">On T=
hu, Oct 29, 2015 at 5:36 PM, Matt Calabrese <span dir=3D"ltr"><<a href=
=3D"mailto:calabrese@google.com" target=3D"_blank">calabrese@google.com</a>=
></span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0 0=
0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div =
class=3D"gmail_extra"><div class=3D"gmail_quote"><span class=3D"">On Thu, O=
ct 29, 2015 at 11:04 AM, Nicol Bolas <span dir=3D"ltr"><<a href=3D"mailt=
o:jmckesson@gmail.com" target=3D"_blank">jmckesson@gmail.com</a>></span>=
wrote:</span><span class=3D""><blockquote class=3D"gmail_quote" style=3D"m=
argin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204=
,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">So... here=
's a proposal that provides mixins, inner classes, and stateless classe=
s to C++. What do you think? Is this a functional idea? What cases did I mi=
ss?</div></blockquote></span></div><br></div><div class=3D"gmail_extra">The=
analysis accompanying the example of CRTP composition that is provided is =
a bit misleading. You can compose such constructs with CRTP fairly easily i=
n practice and the metaphor comparing it to pure virtual functions doesn=
9;t even break down -- all you do is you pass in the derived type in the mi=
ddle base as opposed to middle base's instantiation. In other words, in=
your example, you'd do ":=C2=A0<span style=3D"color:rgb(0,0,0)">p=
ublic A<derived>" rather than ":=C2=A0</span><span style=3D=
"color:rgb(0,0,0)">public A<B<derived>>" if the base needs=
to depend on facilities provided in derived. Maybe there is some other kin=
d of point you are trying to make with the code example there, but it's=
not immediately clear to me. This kind of composition is not too uncommon =
with CRTP.</span></div></div></blockquote><div><br></div><div>Just some adv=
ice -- I wouldn't depend on soft keywords as you are. Unless I'm mi=
sremembering, at Kona it ultimately wasn't even presented by Gor and I =
get the sense that there is little chance it would be accepted.</div></div>=
</div></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a113685e245ce1d052347c617--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 29 Oct 2015 17:50:26 -0700 (PDT)
Raw View
------=_Part_93_1546405029.1446166226829
Content-Type: multipart/alternative;
boundary="----=_Part_94_21967235.1446166226836"
------=_Part_94_21967235.1446166226836
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Thursday, October 29, 2015 at 7:59:37 PM UTC-4, Klaim - Jo=C3=ABl Lamott=
e=20
wrote:
>
> The word "mixin" means different things in different languages.
> The current discussion is confusing because of this.
> I think that what you want is "interface composition" which is not what D=
=20
> calls a mixin.
>
What I have called a mixin in this paper is an accurate representation of=
=20
C++ uses of that term. Googling "C++ mixin" reveals the term "mixin" being=
=20
used for both the reverse-inheritance version and the CRTP-based version.=
=20
There are even several Stack Overflow C++ questions that use the term=20
"mixin" to mean either of these implementations.
My use of terminology is correct within the bounds of C++. That's why I=20
went through a long spiel of explaining exactly what the term means (for=20
the purposes of the proposal) at the start. I do not intend for this=20
feature to match any particular thing in any other language.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
------=_Part_94_21967235.1446166226836
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Thursday, October 29, 2015 at 7:59:37 PM UTC-4, Klaim -=
Jo=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 =
dir=3D"ltr">The word "mixin" means different things in different =
languages.<div>The current discussion is confusing because of this.</div><d=
iv>I think that what you want is "interface composition" which is=
not what D calls a mixin.<br></div></div></blockquote><div><br>What I have=
called a mixin in this paper is an accurate representation of C++ uses of =
that term. Googling "C++ mixin" reveals the term "mixin"=
; being used for both the reverse-inheritance version and the CRTP-based ve=
rsion. There are even several Stack Overflow C++ questions that use the ter=
m "mixin" to mean either of these implementations.<br><br>My use =
of terminology is correct within the bounds of C++. That's why I went t=
hrough a long spiel of explaining exactly what the term means (for the purp=
oses of the proposal) at the start. I do not intend for this feature to mat=
ch any particular thing in any other language.<br></div></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_94_21967235.1446166226836--
------=_Part_93_1546405029.1446166226829--
.
Author: "'Matt Calabrese' via ISO C++ Standard - Future Proposals" <std-proposals@isocpp.org>
Date: Thu, 29 Oct 2015 17:55:44 -0700
Raw View
--001a1134fb0a32dda8052347e3a3
Content-Type: text/plain; charset=UTF-8
On Thu, Oct 29, 2015 at 5:45 PM, Nicol Bolas <jmckesson@gmail.com> wrote:
>
>
> On Thursday, October 29, 2015 at 8:36:44 PM UTC-4, Matt Calabrese wrote:
>>
>> On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <jmck...@gmail.com> wrote:
>>>
>>> So... here's a proposal that provides mixins, inner classes, and
>>> stateless classes to C++. What do you think? Is this a functional idea?
>>> What cases did I miss?
>>>
>>
>> The analysis accompanying the example of CRTP composition that is
>> provided is a bit misleading. You can compose such constructs with CRTP
>> fairly easily in practice and the metaphor comparing it to pure virtual
>> functions doesn't even break down -- all you do is you pass in the derived
>> type in the middle base as opposed to middle base's instantiation. In other
>> words, in your example, you'd do ": public A<derived>" rather than ": public
>> A<B<derived>>" if the base needs to depend on facilities provided in
>> derived.
>>
>
> And what if you want half in half? That is, if B should provide some of
> the facilities that A requires, while `derived` provides the rest?
>
That still works. "derived" publically inherits from "B," so whatever "B"
provides is there in "derived", too. This is not an uncommon usage of CRTP
in existing C++.
On Thu, Oct 29, 2015 at 5:45 PM, Nicol Bolas <jmckesson@gmail.com> wrote:
> That can't happen with the CRTP. One class must implement the full
> requirements. While it may be `derived` rather than `B`, there is no chance
> for one to implement some and one to implement the other.
>
That's not true. This is a common pattern with CRTP. The intermediate base
can provide some definitions and a further child can provide the rest. When
you need this kind of composition of CRTP bases, you just always pass the
furthest required child through each CRTP base. Maybe we're talking about
different problems here, but I don't think so.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--001a1134fb0a32dda8052347e3a3
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div class=3D"gmail_extra"><div class=3D"gmail_quote">On T=
hu, Oct 29, 2015 at 5:45 PM, Nicol Bolas <span dir=3D"ltr"><<a href=3D"m=
ailto:jmckesson@gmail.com" target=3D"_blank">jmckesson@gmail.com</a>></s=
pan> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0p=
x 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-lef=
t-style:solid;padding-left:1ex"><br><br>On Thursday, October 29, 2015 at 8:=
36:44 PM UTC-4, Matt Calabrese wrote:<span class=3D""><blockquote class=3D"=
gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border=
-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div=
dir=3D"ltr"><div><div class=3D"gmail_quote">On Thu, Oct 29, 2015 at 11:04 =
AM, Nicol Bolas <span dir=3D"ltr"><<a rel=3D"nofollow">jmck...@gmail.com=
</a>></span> wrote:<blockquote class=3D"gmail_quote" style=3D"margin:0px=
0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);bor=
der-left-style:solid;padding-left:1ex"><div dir=3D"ltr">So... here's a =
proposal that provides mixins, inner classes, and stateless classes to C++.=
What do you think? Is this a functional idea? What cases did I miss?</div>=
</blockquote></div><br></div><div>The analysis accompanying the example of =
CRTP composition that is provided is a bit misleading. You can compose such=
constructs with CRTP fairly easily in practice and the metaphor comparing =
it to pure virtual functions doesn't even break down -- all you do is y=
ou pass in the derived type in the middle base as opposed to middle base=
9;s instantiation. In other words, in your example, you'd do ":=C2=
=A0<span style=3D"color:rgb(0,0,0)">public A<derived>" rather th=
an ":=C2=A0</span><span style=3D"color:rgb(0,0,0)">public A<B<de=
rived>>" if the base needs to depend on facilities provided in d=
erived.</span></div></div></blockquote></span><div><br>And what if you want=
half in half? That is, if B should provide some of the facilities that A r=
equires, while `derived` provides the rest?<br></div></blockquote><div><br>=
</div><div>That still works. "derived" publically inherits from &=
quot;B," so whatever "B" provides is there in "derived&=
quot;, too. This is not an uncommon usage of CRTP in existing C++.</div><di=
v>=C2=A0</div><div>On Thu, Oct 29, 2015 at 5:45 PM, Nicol Bolas=C2=A0<span =
dir=3D"ltr"><<a href=3D"mailto:jmckesson@gmail.com" target=3D"_blank">jm=
ckesson@gmail.com</a>></span>=C2=A0wrote:</div><blockquote class=3D"gmai=
l_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-lef=
t-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div>Tha=
t can't happen with the CRTP. One class must implement the full require=
ments. While it may be `derived` rather than `B`, there is no chance for on=
e to implement some and one to implement the other.</div></blockquote><div>=
<br></div><div>That's not true. This is a common pattern with CRTP. The=
intermediate base can provide some definitions and a further child can pro=
vide the rest. When you need this kind of composition of CRTP bases, you ju=
st always pass the furthest required child through each CRTP base. Maybe we=
're talking about different problems here, but I don't think so.</d=
iv></div></div></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a1134fb0a32dda8052347e3a3--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 29 Oct 2015 18:07:20 -0700 (PDT)
Raw View
------=_Part_95_1052346111.1446167240254
Content-Type: multipart/alternative;
boundary="----=_Part_96_1735300354.1446167240255"
------=_Part_96_1735300354.1446167240255
Content-Type: text/plain; charset=UTF-8
On Thursday, October 29, 2015 at 8:55:46 PM UTC-4, Matt Calabrese wrote:
>
> On Thu, Oct 29, 2015 at 5:45 PM, Nicol Bolas <jmck...@gmail.com
> <javascript:>> wrote:
>
>> On Thursday, October 29, 2015 at 8:36:44 PM UTC-4, Matt Calabrese wrote:
>>>
>>> On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <jmck...@gmail.com> wrote:
>>>>
>>>> So... here's a proposal that provides mixins, inner classes, and
>>>> stateless classes to C++. What do you think? Is this a functional idea?
>>>> What cases did I miss?
>>>>
>>>
>>> The analysis accompanying the example of CRTP composition that is
>>> provided is a bit misleading. You can compose such constructs with CRTP
>>> fairly easily in practice and the metaphor comparing it to pure virtual
>>> functions doesn't even break down -- all you do is you pass in the derived
>>> type in the middle base as opposed to middle base's instantiation. In other
>>> words, in your example, you'd do ": public A<derived>" rather than ": public
>>> A<B<derived>>" if the base needs to depend on facilities provided in
>>> derived.
>>>
>>
>> And what if you want half in half? That is, if B should provide some of
>> the facilities that A requires, while `derived` provides the rest?
>>
>
> That still works. "derived" publically inherits from "B," so whatever "B"
> provides is there in "derived", too. This is not an uncommon usage of CRTP
> in existing C++.
>
I hadn't realized that. Fair enough.
On Thursday, October 29, 2015 at 8:47:40 PM UTC-4, Matt Calabrese wrote:
>
> Just some advice -- I wouldn't depend on soft keywords as you are. Unless
> I'm misremembering, at Kona it ultimately wasn't even presented by Gor and
> I get the sense that there is little chance it would be accepted.
>
It's not dependent on soft keywords. They're just the easiest way to avoid
the generally pointless questions of "how do you specify it" rather than
the more fruitful questions of "do we want this and what behavior should it
have?" Much like P0057 uses <await-key-word> and so forth as placeholders
for the actual syntax.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_96_1735300354.1446167240255
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Thursday, October 29, 2015 at 8:55:46 PM UTC-4, Matt Calabrese wrote:<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><div class=
=3D"gmail_quote">On Thu, Oct 29, 2015 at 5:45 PM, Nicol Bolas <span dir=3D"=
ltr"><<a href=3D"javascript:" target=3D"_blank" gdf-obfuscated-mailto=3D=
"2-bo06RVEwAJ" rel=3D"nofollow" onmousedown=3D"this.href=3D'javascript:=
';return true;" onclick=3D"this.href=3D'javascript:';return tru=
e;">jmck...@gmail.com</a>></span> wrote:<br><blockquote class=3D"gmail_q=
uote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-c=
olor:rgb(204,204,204);border-left-style:solid;padding-left:1ex">On Thursday=
, October 29, 2015 at 8:36:44 PM UTC-4, Matt Calabrese wrote:<span><blockqu=
ote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-wid=
th:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-l=
eft:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_quote">On Thu, Oct 29, 2=
015 at 11:04 AM, Nicol Bolas <span dir=3D"ltr"><<a rel=3D"nofollow">jmck=
....@gmail.com</a>></span> wrote:<blockquote class=3D"gmail_quote" style=
=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(20=
4,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">So...=
here's a proposal that provides mixins, inner classes, and stateless c=
lasses to C++. What do you think? Is this a functional idea? What cases did=
I miss?</div></blockquote></div><br></div><div>The analysis accompanying t=
he example of CRTP composition that is provided is a bit misleading. You ca=
n compose such constructs with CRTP fairly easily in practice and the metap=
hor comparing it to pure virtual functions doesn't even break down -- a=
ll you do is you pass in the derived type in the middle base as opposed to =
middle base's instantiation. In other words, in your example, you'd=
do ":=C2=A0<span style=3D"color:rgb(0,0,0)">public A<derived>&q=
uot; rather than ":=C2=A0</span><span style=3D"color:rgb(0,0,0)">publi=
c A<B<derived>>" if the base needs to depend on facilities=
provided in derived.</span></div></div></blockquote></span><div><br>And wh=
at if you want half in half? That is, if B should provide some of the facil=
ities that A requires, while `derived` provides the rest?<br></div></blockq=
uote><div><br></div><div>That still works. "derived" publically i=
nherits from "B," so whatever "B" provides is there in =
"derived", too. This is not an uncommon usage of CRTP in existing=
C++.</div></div></div></div></blockquote><div><br>I hadn't realized th=
at. Fair enough.<br><br>On Thursday, October 29, 2015 at 8:47:40 PM UTC-4, =
Matt Calabrese wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;m=
argin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=
=3D"ltr"><div><div class=3D"gmail_quote"><div>Just
some advice -- I wouldn't depend on soft keywords as you are. Unless=
=20
I'm misremembering, at Kona it ultimately wasn't even presented by =
Gor=20
and I get the sense that there is little chance it would be accepted.</div>=
</div></div></div>
</blockquote><br>It's not dependent on soft keywords. They're just =
the easiest way to avoid the generally pointless questions of "how do =
you specify it" rather than the more fruitful questions of "do we=
want this and what behavior should it have?" Much like P0057 uses <=
;await-key-word> and so forth as placeholders for the actual syntax.<br>=
</div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_96_1735300354.1446167240255--
------=_Part_95_1052346111.1446167240254--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 30 Oct 2015 09:40:17 -0700 (PDT)
Raw View
------=_Part_990_1734063990.1446223217498
Content-Type: multipart/alternative;
boundary="----=_Part_991_315465743.1446223217499"
------=_Part_991_315465743.1446223217499
Content-Type: text/plain; charset=UTF-8
On Thursday, October 29, 2015 at 9:07:20 PM UTC-4, Nicol Bolas wrote:
>
> On Thursday, October 29, 2015 at 8:55:46 PM UTC-4, Matt Calabrese wrote:
>>
>> On Thu, Oct 29, 2015 at 5:45 PM, Nicol Bolas <jmck...@gmail.com> wrote:
>>
>>> On Thursday, October 29, 2015 at 8:36:44 PM UTC-4, Matt Calabrese wrote:
>>>>
>>>> On Thu, Oct 29, 2015 at 11:04 AM, Nicol Bolas <jmck...@gmail.com>
>>>> wrote:
>>>>>
>>>>> So... here's a proposal that provides mixins, inner classes, and
>>>>> stateless classes to C++. What do you think? Is this a functional idea?
>>>>> What cases did I miss?
>>>>>
>>>>
>>>> The analysis accompanying the example of CRTP composition that is
>>>> provided is a bit misleading. You can compose such constructs with CRTP
>>>> fairly easily in practice and the metaphor comparing it to pure virtual
>>>> functions doesn't even break down -- all you do is you pass in the derived
>>>> type in the middle base as opposed to middle base's instantiation. In other
>>>> words, in your example, you'd do ": public A<derived>" rather than ": public
>>>> A<B<derived>>" if the base needs to depend on facilities provided in
>>>> derived.
>>>>
>>>
>>> And what if you want half in half? That is, if B should provide some of
>>> the facilities that A requires, while `derived` provides the rest?
>>>
>>
>> That still works. "derived" publically inherits from "B," so whatever "B"
>> provides is there in "derived", too. This is not an uncommon usage of CRTP
>> in existing C++.
>>
>
> I hadn't realized that. Fair enough.
>
Thinking about it more, this raises a question about how such a language
feature *should* work.
As it stands, when using the pure CRTP method, each mixin in a composition
has to forward the derived class through to its mixin bases. In the above
code, the `derived` class has ultimate priority with regard to its
interface. That is, if both `B` and `derived` implement the same feature
that's used by `A`, then the `derived` one is the one that gets called.
Is this the behavior we would actually *want*, though? The current behavior
is used if for no other reason than that it's the only way to make it
actually work. Whereas if the feature is a first-class language feature, we
could implement it either way.
On the one hand, the derived-first behavior mirrors that of a virtual
function: the most derived class has ultimate priority over the interface.
On the other hand, the general idea here is the inversion of the usual
control relationship. As such, the least derived class should have ultimate
priority. And if you think about it, it kind of has to. After all, if `A`
tries to call a method that `A` itself implements, it ought to always call
`A`'s version, regardless of what `derived` does.
As a first-class language feature, there is no explicit `self()` method to
call. So the user does not decide when things propagate up the inheritance
chain. The compiler must decide, for each name lookup, if you meant
something in this class or something in one of the derived classes.
With the current CRTP-based system, the rule would have to be "check my
class, then jump to the most derived class, then to his parents, etc." The
fully inverted way would be "check my class, then my mixin parent, etc."
I liked the simplicity of the fully inverted method. I just don't know if
it is the right way to do it, the way users would *expect* inverted
relationships to work.
Then again, your example does seem to raise an interesting issue of scope.
Consider these mixins, implemented the current way:
template<typename derived>
struct A
{
//CRTP stuff
};
template<typename derived>
struct B : public A<B<derived>>
{
//CRTP stuff
};
template<typename derived>
struct C : public A<derived>
{
//CRTP stuff
};
In this example, `B` and `C` represent two distinct and useful uses of
mixins.
`B` is declaring itself to be a mixin. But it just so happens that some of
its functionality is implemented via another mixin. This is not something
`B` wants to expose to its users, so `B` explicitly isolates its users from
`A`. Therefore, `A`'s reach is restricted to `B`.
`C` is declaring itself to be a mixin that is *composed* of itself and
another mixin. `A` is being allowed to access whoever it is that `C` is
mixed into.
One is "implemented with;" the other is "mixin composition." My current
proposal has no equivalent to this. It uses public/private inheritance to
decide when the reach ends, but that's obviously too heavy handed (`A`
would be unable to augment `B`'s public interface).
I'm going to have to come up with a solution for this, as it seems like an
incredibly useful distinction to be able to make.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_991_315465743.1446223217499
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Thursday, October 29, 2015 at 9:07:20 PM UTC-4, Nicol Bolas wrote:<block=
quote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-le=
ft: 1px #ccc solid;padding-left: 1ex;">On Thursday, October 29, 2015 at 8:5=
5:46 PM UTC-4, Matt Calabrese 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 class=3D"gmail_quote">On Thu, Oct 29, 2015 at 5=
:45 PM, Nicol Bolas <span dir=3D"ltr"><<a rel=3D"nofollow">jmck...@gmail=
..com</a>></span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"ma=
rgin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,=
204);border-left-style:solid;padding-left:1ex">On Thursday, October 29, 201=
5 at 8:36:44 PM UTC-4, Matt Calabrese wrote:<span><blockquote class=3D"gmai=
l_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-lef=
t-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=
=3D"ltr"><div><div class=3D"gmail_quote">On Thu, Oct 29, 2015 at 11:04 AM, =
Nicol Bolas <span dir=3D"ltr"><<a rel=3D"nofollow">jmck...@gmail.com</a>=
></span> wrote:<blockquote class=3D"gmail_quote" style=3D"margin:0px 0px=
0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-=
left-style:solid;padding-left:1ex"><div dir=3D"ltr">So... here's a prop=
osal that provides mixins, inner classes, and stateless classes to C++. Wha=
t do you think? Is this a functional idea? What cases did I miss?</div></bl=
ockquote></div><br></div><div>The analysis accompanying the example of CRTP=
composition that is provided is a bit misleading. You can compose such con=
structs with CRTP fairly easily in practice and the metaphor comparing it t=
o pure virtual functions doesn't even break down -- all you do is you p=
ass in the derived type in the middle base as opposed to middle base's =
instantiation. In other words, in your example, you'd do ":=C2=A0<=
span style=3D"color:rgb(0,0,0)">public A<derived>" rather than &=
quot;:=C2=A0</span><span style=3D"color:rgb(0,0,0)">public A<B<derive=
d>>" if the base needs to depend on facilities provided in deriv=
ed.</span></div></div></blockquote></span><div><br>And what if you want hal=
f in half? That is, if B should provide some of the facilities that A requi=
res, while `derived` provides the rest?<br></div></blockquote><div><br></di=
v><div>That still works. "derived" publically inherits from "=
;B," so whatever "B" provides is there in "derived"=
;, too. This is not an uncommon usage of CRTP in existing C++.</div></div><=
/div></div></blockquote><div><br>I hadn't realized that. Fair enough.<b=
r></div></blockquote><div><br></div>Thinking about it more, this raises a q=
uestion about how such a language feature <i>should</i> work.<br><br>As it =
stands, when using the pure CRTP method, each mixin in a composition has to=
forward the derived class through to its mixin bases. In the above code, t=
he `derived` class has ultimate priority with regard to its interface. That=
is, if both `B` and `derived` implement the same feature that's used b=
y `A`, then the `derived` one is the one that gets called.<br><br>Is this t=
he behavior we would actually <i>want</i>, though? The current behavior is =
used if for no other reason than that it's the only way to make it actu=
ally work. Whereas if the feature is a first-class language feature, we cou=
ld implement it either way.<br><br>On the one hand, the derived-first behav=
ior mirrors that of a virtual function: the most derived class has ultimate=
priority over the interface.<br><br>On the other hand, the general idea he=
re is the inversion of the usual control relationship. As such, the least d=
erived class should have ultimate priority. And if you think about it, it k=
ind of has to. After all, if `A` tries to call a method that `A` itself imp=
lements, it ought to always call `A`'s version, regardless of what `der=
ived` does.<br><br>As a first-class language feature, there is no explicit =
`self()` method to call. So the user does not decide when things propagate =
up the inheritance chain. The compiler must decide, for each name lookup, i=
f you meant something in this class or something in one of the derived clas=
ses.<br><br>With the current CRTP-based system, the rule would have to be &=
quot;check my class, then jump to the most derived class, then to his paren=
ts, etc." The fully inverted way would be "check my class, then m=
y mixin parent, etc."<br><br>I liked the simplicity of the fully inver=
ted method. I just don't know if it is the right way to do it, the way =
users would <i>expect</i> inverted relationships to work.<br><br>Then again=
, your example does seem to raise an interesting issue of scope. Consider t=
hese mixins, implemented the current way:<br><br><div class=3D"prettyprint"=
style=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 187,=
187); border-style: solid; border-width: 1px; word-wrap: break-word;"><cod=
e class=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"color:=
#008;" class=3D"styled-by-prettify">template</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify"><</span><span style=3D"color: #008;" =
class=3D"styled-by-prettify">typename</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"> derived</span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">></span><span style=3D"color: #000;" class=3D"s=
tyled-by-prettify"><br></span><span style=3D"color: #008;" class=3D"styled-=
by-prettify">struct</span><span style=3D"color: #000;" class=3D"styled-by-p=
rettify"> A<br></span><span style=3D"color: #660;" class=3D"styled-by-prett=
ify">{</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=
=C2=A0 </span><span style=3D"color: #800;" class=3D"styled-by-prettify">//C=
RTP stuff</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><=
br></span><span style=3D"color: #660;" class=3D"styled-by-prettify">};</spa=
n><span style=3D"color: #000;" class=3D"styled-by-prettify"><br><br></span>=
<span style=3D"color: #008;" class=3D"styled-by-prettify">template</span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify"><</span><span st=
yle=3D"color: #008;" class=3D"styled-by-prettify">typename</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify"> derived</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">></span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #=
008;" class=3D"styled-by-prettify">struct</span><span style=3D"color: #000;=
" class=3D"styled-by-prettify"> B </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">:</span><span style=3D"color: #000;" class=3D"style=
d-by-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-pret=
tify">public</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"> A</span><span style=3D"color: #660;" class=3D"styled-by-prettify"><</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify">B</span><spa=
n style=3D"color: #080;" class=3D"styled-by-prettify"><derived></span=
><span style=3D"color: #660;" class=3D"styled-by-prettify">></span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"colo=
r: #800;" class=3D"styled-by-prettify">//CRTP stuff</span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">};</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"><br><br></span><span style=3D"color: #008;" clas=
s=3D"styled-by-prettify">template</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify"><</span><span style=3D"color: #008;" class=3D"st=
yled-by-prettify">typename</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"> derived</span><span style=3D"color: #660;" class=3D"styled=
-by-prettify">></span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify"><br></span><span style=3D"color: #008;" class=3D"styled-by-prettify=
">struct</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> C=
</span><span style=3D"color: #660;" class=3D"styled-by-prettify">:</span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span styl=
e=3D"color: #008;" class=3D"styled-by-prettify">public</span><span style=3D=
"color: #000;" class=3D"styled-by-prettify"> A</span><span style=3D"color: =
#080;" class=3D"styled-by-prettify"><derived></span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"color: #800;" cl=
ass=3D"styled-by-prettify">//CRTP stuff</span><span style=3D"color: #000;" =
class=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">};</span></div></code></div><br>In this example, `B=
` and `C` represent two distinct and useful uses of mixins.<br><br>`B` is d=
eclaring itself to be a mixin. But it just so happens that some of its func=
tionality is implemented via another mixin. This is not something `B` wants=
to expose to its users, so `B` explicitly isolates its users from `A`. The=
refore, `A`'s reach is restricted to `B`.<br><br>`C` is declaring itsel=
f to be a mixin that is <i>composed</i> of itself and another mixin. `A` is=
being allowed to access whoever it is that `C` is mixed into.<br><br>One i=
s "implemented with;" the other is "mixin composition."=
My current proposal has no equivalent to this. It uses public/private inhe=
ritance to decide when the reach ends, but that's obviously too heavy h=
anded (`A` would be unable to augment `B`'s public interface).<br><br>I=
'm going to have to come up with a solution for this, as it seems like =
an incredibly useful distinction to be able to make.<br>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_991_315465743.1446223217499--
------=_Part_990_1734063990.1446223217498--
.
Author: Andrew Tomazos <andrewtomazos@gmail.com>
Date: Sat, 31 Oct 2015 03:47:18 +0100
Raw View
--047d7bfced30099e9e05235d90d8
Content-Type: text/plain; charset=UTF-8
This proposal is too long to have an inadequate summary/abstract.
I would advise to replace the text "Two of these features are really two
halves of the same conceptual coin (though they are separate features). The
third feature is an optimization for certain use cases that make the other
two features much more attractive."
.... with ...
"The first is <X>. <X> is .... *one or two sentences describing X*. The
second is <Y>. <Y> is... *one or two sentences describing Y*. The third
is <Z>. <Z> is... *one or two sentences describing Z*."
Where X, Y and Z might be "Mixin Class", "Inner Class" and "Stateless
Class" - for example - if those are the three features you allude to in the
opening sentence.
On Thu, Oct 29, 2015 at 7:04 PM, Nicol Bolas <jmckesson@gmail.com> wrote:
> A while back, we had the semi-annual "let's put properties in C++
> discussion
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/n0QcD_K5dBY/3ZW3MpJrmhIJ>".
> Unlike most such discussions however, that led to a more generalized idea: adding
> with a restrictive form of Java-style inner classes
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/Yorc58iiBwAJ>,
> which could be used to implement properties sanely. Several more concrete
> ideas
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/G5MsctCECAAJ>
> were put forth along these lines, including one of my own that introduced
> a separate idea for stateless types
> <https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/gxAxe5xBAQAJ>
> .
>
> However, that idea hit a large snag, in that inner classes have to be
> nested in the classes they use. So if you're trying to implement something
> complex like properties, you'd have to add tons of boilerplate code to make
> the inner class proxy work. For every property you write. That's not to say
> that inner classes are all about properties, but that is a use case of them.
>
> The original inline class idea had the notion of being able to declare
> such constructs outside of a class, but they had some limitations. Then, I
> started playing around with the idea of allowing "inner classes" to be
> declared outside of a type, but as a template. One of the template
> parameters would be filled in with the actual class it is an inner class
> of. So the common boilerplate could go into there.
>
> And then, I realized that I was really just solving the CRTP problem. That
> is, I was declaring a way to write a class that would be used by another
> class, injecting its definitions into the other class as if they were one
> type.
>
> In short, I'd created language support for C++ mixins. Indeed, I realized
> that inner classes and mixins are really kinda the same thing, if you think
> about their effects.
>
> So... here's a proposal that provides mixins, inner classes, and stateless
> classes to C++. What do you think? Is this a functional idea? What cases
> did I miss?
>
> --
>
> ---
> 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.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--047d7bfced30099e9e05235d90d8
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">This proposal is too long to have an inadequate summary/ab=
stract.<div><br></div><div>I would advise to replace the text "Two of =
these features are really two halves of the same conceptual coin (though th=
ey are separate features). The third feature is an optimization for certain=
use cases that make the other two features much more attractive."<br>=
</div><div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">=
.... with ...</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_=
extra">"The first is <X>. <X> is .... *one or two sentence=
s describing X*.=C2=A0 The second is <Y>. =C2=A0<Y> is... *one =
or two sentences=C2=A0describing Y*.=C2=A0 The third is <Z>. =C2=A0&l=
t;Z> is... *one or two sentences describing Z*."</div><div class=3D=
"gmail_extra"><br></div><div class=3D"gmail_extra">Where X, Y and Z might b=
e "Mixin Class", "Inner Class" and "Stateless Clas=
s" - for example - if those are the three features you allude to in th=
e opening sentence.</div><div class=3D"gmail_extra"><br></div><div class=3D=
"gmail_extra"><br><div class=3D"gmail_quote">On Thu, Oct 29, 2015 at 7:04 P=
M, Nicol Bolas <span dir=3D"ltr"><<a href=3D"mailto:jmckesson@gmail.com"=
target=3D"_blank">jmckesson@gmail.com</a>></span> wrote:<br><blockquote=
class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:=
1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left=
:1ex"><div dir=3D"ltr">A while back, we had the semi-annual "<a href=
=3D"https://groups.google.com/a/isocpp.org/d/msg/std-proposals/n0QcD_K5dBY/=
3ZW3MpJrmhIJ" target=3D"_blank">let's put properties in C++ discussion<=
/a>". Unlike most such discussions however, that led to a more general=
ized idea: <a href=3D"https://groups.google.com/a/isocpp.org/d/msg/std-prop=
osals/u35GIuJECcQ/Yorc58iiBwAJ" target=3D"_blank">adding with a restrictive=
form of Java-style inner classes</a>, which could be used to implement pro=
perties sanely. Several <a href=3D"https://groups.google.com/a/isocpp.org/d=
/msg/std-proposals/u35GIuJECcQ/G5MsctCECAAJ" target=3D"_blank">more concret=
e ideas</a> were put forth along these lines, including one of my own that =
<a href=3D"https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GI=
uJECcQ/gxAxe5xBAQAJ" target=3D"_blank">introduced a separate idea for state=
less types</a>.<br><br>However, that idea hit a large snag, in that inner c=
lasses have to be nested in the classes they use. So if you're trying t=
o implement something complex like properties, you'd have to add tons o=
f boilerplate code to make the inner class proxy work. For every property y=
ou write. That's not to say that inner classes are all about properties=
, but that is a use case of them.<br><br>The original inline class idea had=
the notion of being able to declare such constructs outside of a class, bu=
t they had some limitations. Then, I started playing around with the idea o=
f allowing "inner classes" to be declared outside of a type, but =
as a template. One of the template parameters would be filled in with the a=
ctual class it is an inner class of. So the common boilerplate could go int=
o there.<br><br>And then, I realized that I was really just solving the CRT=
P problem. That is, I was declaring a way to write a class that would be us=
ed by another class, injecting its definitions into the other class as if t=
hey were one type.<br><br>In short, I'd created language support for C+=
+ mixins. Indeed, I realized that inner classes and mixins are really kinda=
the same thing, if you think about their effects.<br><br>So... here's =
a proposal that provides mixins, inner classes, and stateless classes to C+=
+. What do you think? Is this a functional idea? What cases did I miss?<spa=
n><font color=3D"#888888"><br></font></span></div><span><font color=3D"#888=
888">
<p></p>
-- <br>
<br>
--- <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" target=3D"_=
blank">std-proposals+unsubscribe@isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org" target=3D"_blank">std-proposals@isocpp.org</a>.<br>
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/" target=3D"_blank">http://groups.google.com/a/isocpp.org/gro=
up/std-proposals/</a>.<br>
</font></span></blockquote></div><br></div></div></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--047d7bfced30099e9e05235d90d8--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sat, 31 Oct 2015 06:05:31 -0700 (PDT)
Raw View
------=_Part_1776_1976890151.1446296731960
Content-Type: multipart/alternative;
boundary="----=_Part_1777_66134838.1446296731960"
------=_Part_1777_66134838.1446296731960
Content-Type: text/plain; charset=UTF-8
On Friday, October 30, 2015 at 10:47:22 PM UTC-4, Andrew Tomazos wrote:
>
> This proposal is too long to have an inadequate summary/abstract.
>
> I would advise to replace the text "Two of these features are really two
> halves of the same conceptual coin (though they are separate features). The
> third feature is an optimization for certain use cases that make the other
> two features much more attractive."
>
> ... with ...
>
> "The first is <X>. <X> is .... *one or two sentences describing X*. The
> second is <Y>. <Y> is... *one or two sentences describing Y*. The third
> is <Z>. <Z> is... *one or two sentences describing Z*."
>
> Where X, Y and Z might be "Mixin Class", "Inner Class" and "Stateless
> Class" - for example - if those are the three features you allude to in the
> opening sentence.
>
That's a good point. The reason I hold off on talking about what's actually
in it is because it looks like 3 separate features that have nothing to do
with one another. So I thought my first goal should be to explain how
they're related and how each one complements the other.
That probably made more sense when it was only a 20KB document instead of a
60KB one ;)
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_1777_66134838.1446296731960
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Friday, October 30, 2015 at 10:47:22 PM UTC-4, Andrew Tomazos wrote:<blo=
ckquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-=
left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">This proposal is =
too long to have an inadequate summary/abstract.<div><br></div><div>I would=
advise to replace the text "Two of these features are really two halv=
es of the same conceptual coin (though they are separate features). The thi=
rd feature is an optimization for certain use cases that make the other two=
features much more attractive."<br></div><div><div><br></div><div>...=
with ...</div><div><br></div><div>"The first is <X>. <X> =
is .... *one or two sentences describing X*.=C2=A0 The second is <Y>.=
=C2=A0<Y> is... *one or two sentences=C2=A0describing Y*.=C2=A0 The =
third is <Z>. =C2=A0<Z> is... *one or two sentences describing =
Z*."</div><div><br></div><div>Where X, Y and Z might be "Mixin Cl=
ass", "Inner Class" and "Stateless Class" - for ex=
ample - if those are the three features you allude to in the opening senten=
ce.</div></div></div></blockquote><div><br>That's a good point. The rea=
son I hold off on talking about what's actually in it is because it loo=
ks like 3 separate features that have nothing to do with one another. So I =
thought my first goal should be to explain how they're related and how =
each one complements the other.<br><br>That probably made more sense when i=
t was only a 20KB document instead of a 60KB one ;)</div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_1777_66134838.1446296731960--
------=_Part_1776_1976890151.1446296731960--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 3 Nov 2015 13:39:21 -0800 (PST)
Raw View
------=_Part_821_928106953.1446586761710
Content-Type: multipart/alternative;
boundary="----=_Part_822_1147234851.1446586761711"
------=_Part_822_1147234851.1446586761711
Content-Type: text/plain; charset=UTF-8
Here's an improved version of the proposal. Changelist:
* Altered intro and restructured the document, putting uses after
concepts.
* Removed erroneous claims about current CRTP implementations.
* Added functionality for explicitly restricting the reach of a mixin.
* Added discussion of mixin overriding inversion.
* Added section on alternative designs in other languages.
* Subsection on how inner class offsets are generated.
* Added section on changes.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_822_1147234851.1446586761711
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">Here's an improved version of the proposal. Changelist=
:<br><br>=C2=A0=C2=A0=C2=A0 * Altered intro and restructured the document, =
putting uses after concepts.<br>=C2=A0=C2=A0=C2=A0 * Removed erroneous clai=
ms about current CRTP implementations.<br>=C2=A0=C2=A0=C2=A0 * Added functi=
onality for explicitly restricting the reach of a mixin.<br>=C2=A0=C2=A0=C2=
=A0 * Added discussion of mixin overriding inversion.<br>=C2=A0=C2=A0=C2=A0=
* Added section on alternative designs in other languages.<br>=C2=A0=C2=A0=
=C2=A0 * Subsection on how inner class offsets are generated.<br>=C2=A0=C2=
=A0=C2=A0 * Added section on changes.<br></div>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_822_1147234851.1446586761711--
------=_Part_821_928106953.1446586761710
Content-Type: text/x-markdown; charset=US-ASCII;
name="Mixins, Inner, and Stateless Classess.md"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Mixins, Inner, and Stateless Classess.md"
X-Attachment-Id: 2d7dbc59-05a7-43f6-846b-679fad7f692d
Content-ID: <2d7dbc59-05a7-43f6-846b-679fad7f692d>
% Mixin, Inner and Stateless Classes, v0.6
%
% November 3, 2015
This proposal specifies three new language features associated with classes. Two of these provide an inversion of the control of class subobjects. Mixin classes allow a (template) class to access the members of the class that derives from it, while inner classes are provided access to the members of the objects that contain them. Stateless classes are classes which, when used as subobjects of other types, take up no space in those objects. They are useful mainly when applied to mixin or inner classes, which frequently rely on storage provided by the client class, having no non-static data members of their own.
# Concepts
## Inverted Subobject
A subobject is itself an object, but it is wholly controlled by the object in which it is contained. For the purposes of this discussion, we will consider two kinds of subobjects in C++: non-static data members (NSDMs) and base class members. An NSDM adds to the capabilities of the object to which it is a member by containment. Users talk to members, and the members implement their own data storage and APIs. Base class subobjects are similar in that they expand the capabilities of the containing class. However, they expand those capabilities directly, bringing all of their functionality into the direct interface of the derived class. Both of them have access controls which limit to whom such functionality is exposed.
With subobjects of all kinds, control flows in one direction: from the containing object to the objects it contains. Base classes can access their own members as well as any members of one of their base classes. But no base class can (directly) access members of its derived class. Similarly, NSDMs can access any NSDMs of their own, but they cannot access the interface of the object that contains them.
What this proposal suggests doing is changing this relationship. An "inverted subobject" is one where the relationship between the container and the contained objects is inverted. Inverted base subobjects can access their (direct) owning object, and inverted NSDM subobjects can access the object that they are contained within.
It turns out that both of these kinds of control inversions are in use today, either in C++ itself or in other languages. While conceptually these are just variations of the same thing, these features are often implemented as two distinct features with two distinct names.
Java provides the name for an NSDM that can access its owning class: inner class. The other concept's name is defined by the effects of the concept. If a base class can access the derived class's members, then it can add functionality to that class which integrates seamlessly with any particular implementation that provides the appropriate interface. Let us refer to this as a mixin[^1].
Note that mixins effectively allow a form of static polymorphism. The normal way to invert relationships in a hierarchy is to use `virtual` functions. The derived class implements the virtual interface declared by the base class. But this can be inefficient, due to the overhead of virtual calls.
Mixins effectively allow the same inversion in a static way. Not unsurprisingly, mixins in C++ are usually implemented using the static polymorphism tool of C++: templates.
# Uses
## Stateless Classes
Stateless classes by themselves have limited utility, but they are hardly useless. For example, iterator categories are tested currently via the presence of certain base class tags. These tags have no state besides the fact that they exist. These and similar types are reasonable use cases for statelessness without inverted classes.
## Mixins
The uses for mixins are legion; they are a general-purpose tool for adding data/functions to a class from outside of the class itself. That basic feature can be used for things like C# partial classes, where some data and/or interface is generated from a tool. This can be done today, but full mixin support allows the generated class to interact with the main class as though they were one class.
Mixins are used liberally within the range TS proposal, emulated as best as possible with C++. Providing full language support for them would be a boon to both the implementation of that library and users who want to use its facade and view mixins.
The ability to make mixins stateless where appropriate is the icing on the cake, as it allows the containing class to provide data storage for the mixin's interface. The range TS mixins are good candidates for this optimization, as they usually have no members.
The standard already has a few examples of mixin-like classes, which could be improved with language support. The class `enable_shared_from_this` is a mixin with its own storage; it effectively stores a `weak_ptr` (or the guts of one) in the class.
If it were a true mixin, it could be augmented with a SFINAE test of the derived type for a certain interface. If the derived class provides that interface, then it would refrain from storing data itself; it would instead store the `weak_ptr` through the derived class's interface. This is important because classes that use `enable_shared_from_this` cannot be standard layout. Whereas with true mixin support, the class could be empty or stateless, allowing the derived class to retain its layout compatibility.
Using the derived class's storage would also allow the derived class to access the `weak_ptr` directly. This would avoid the current method of doing `weak_ptr<T>(shared_from_this())`, which involves a needless quantity of atomic operations.
And this control inversion comes without any virtual overhead, which is the primary reason why `enable_shared_from_this` did not provide a virtual interface for such storage.
## Properties {#uses_properties}
Inner classes also have many uses, though they are not as immediately apparent.
Full disclosure: I am personally not a big fan of properties. However, I also know that this is a thing people want, and the thread/proposals that led to me developing this one were primarily about getting properties into the language somehow. Obviously, this feature has evolved since then.
Stateless inner classes make it possible for users to implement properties with zero overhead. Prior property implementations also seemed really hackey; they would require things like lambdas as initializers to some struct, or the property itself would have to hold the value (thus making it impossible for properties to represent things that were not values). With stateless inner classes, the property implementation has full access to most of the powers of the C++ object model, without a bunch of boilerplate to access the data.
They can overload `operator=` to perform assignment as the "setter" operation. They can overload `operator Typename` for implicit conversions as the "getter" operation. If the underlying value is of an appropriate type, they can even overload math assignment operators like `operator+=` and such. In effect, the property object can be a full proxy for the real thing, providing access, modification, or both, in all of the ways that C++ allows. And all without taking up added space.
Because there is a lot of boilerplate here to make a good property that plays by C++'s rules, this boilerplate can almost certainly be abstracted out... into a stateless *mixin*. The inner class property itself would be responsible only for providing a `get` function and a `set` function. The mixin is what would do all of the work for operator overloading and such.
Template polymorphism can be used here as well to improve the interface. For example, if the return value of `get` is a pointer (or a type that implements `operator->`), the property can synthesize an appropriate `operator->` overload. If no `set` method is provided, then the property does not synthesize assignment operators. If no `get` method is provided, the mixin does not allow data access. But more complex rigging can be accomplished as well.
If there is a non-const `get` overload that returns a reference, and there is no `set` method, then the mixin assumes that `get() = expr` is an acceptable means of changing the value. So its overloaded assignment operators will be synthesized by calling the non-const `get`. Furthermore, a non-const `get` allows the system to synthesize the `operator+=` and similar operators without having a `get`/`set` round-trip.
And thus, a property definition in a class would look like this:
//Mixin usage with anonymous classes.
stateless inner class : property<mixin>
{
//Private members are OK; `property` is implicitly a friend.
const float &get() const {return value;}
//non-const reference means `property` will use this to set as well.
float &get() {return value;}
} property_name;
The mixin does all of the complex work that synthesizes the appropriate operator overloads. The prefix part could even be wrapped up in a macro.
## Proxies and Interface Translation
Properties are really just a specific instance of a more general use case for stateless inner classes: interface translation without creating new proxy objects.
Let's say that you have some container class `C`. It has an interface that works for you and your needs. But with the ranges proposal, you find that it doesn't really fit into that paradigm. It is conceptually a range, but it doesn't actually fit the range paradigm. And you don't want to rewrite your interface; if it's not broken, don't fix it. And writing the range interface directly in `C` is a problem due to interface conflicts, as well as simply cluttering up the class definition.
The way to resolve that today would be to create some proxy object `PC`, which internally stores a pointer/reference to a `C` instance. This object would translate the interface of `C` into one appropriate for the ranges proposal.
A more pressing concern is one of lifetime. Because `PC` is an object type, which the user can declare values of, it is now possible to take a `PC` and possibly store it long term. And since it stores a pointer/reference to `C`, that makes it way too easy for someone to think that they have a real object rather than a proxy.
If the proxy object is an inner class instance however, the proxy *cannot* be stored. A reference to it can be stored, but then it is abundantly clear that you're storing a *reference*, not a container-like object. And thus, it's clear to everyone that you have stored a reference to some other object, which is what virtually every proxy object is.
It also alleviates the small overhead to using proxies. A regular proxy object has to be constructed every time a proxy is created. Whereas an inner class proxy is a fixed part of the class, rather than generated as needed by the user. It can be stateless, with all of the data it's accessing stored within its owner. This saving an indirection, since getting the pointer to its owner is a static operation rather than a memory access.
Proxy iterators are a good place to see where this overhead could be non-trivial. Particularly in proxy output iterators, every time you do `*it++ = ...`, you call the proxy's constructor, which copies a pointer/reference to whatever is needed into the proxy object. It gets used once to do the copy, then destroyed.
With inner classes, the proxy type can be a stateless inner class of the iterator type itself. So `operator*` returns a reference to the inner class. The inner class's `operator=` will store the value, possibly updating the position of the iterator. And this neatly avoids having to create and destroy an object every time insertion happens.
Aggressive compiler optimization may remove these concerns, but inner classes ensure that they don't exist, even in debug builds.
This kind of translation also makes it possible to provide different ways to view the same data. An example used to illustrate this was a class that stores colors. The class would internally store the color in the sRGB colorspace, but through a stateless inner class interface, you could modify the value in the HSV colorspace.
Without inner classes, this would require a bunch of explicit calls to conversion functions: `color.set_hsv(color.get_hsv().set_hue(new_hue))`. With stateless inner classes, it requires `color.hsv.hue = new_hue`. This is much cleaner and clearer to the user. And probably more efficient.
One could even allow `color.hsv.hue += new_hue`. Without the inner class, a similar operation would have either required an explicit `add_hue` function or the user would need to do this:
auto &&hsv = color.get_hsv(); //Avoid double get call.
color.set_hsv(hsv.set_hue(hsv.get_hue() + new_hue));
This takes up two lines now. If you accept the double get, you get the gargantuan expression:
color.set_hsv(color.get_hsv().set_hue(color.get_hsv.get_hue() + new_hue)
And remember: all of these inner classes can be *stateless*. All they do is enhance the class's interface; they impose no overhead on their containing classes.
Not every set of types with different representations should use such views. For example, [Howard Hinnant's Date/Time library](https://github.com/HowardHinnant/date) provides `day_point` and `year_month_day`, which are two versions of the same data. I would not suggest giving `day_point` a view that provides `year_month_day`'s interface; they should be two separate types, due to the conversion overhead.
Though it could still have property-like objects for setting/getting dates in different formats.
# Current Implementations
Inner classes and mixins can be implemented in the language as it currently stands. The question is what kind of gymnastics are required to implement them, as well as the ability to compose them from different library implementations. And of course how easy it is to get the implementation wrong.
## Mixin
For our purposes, mixin implementations should allow:
1. The mixin class to access the members of the class it "mixes" into. How much access is a question, but even just public access would be functional.
2. The class being mixed into can access the members of the mixin. Again, just public members would be something.
3. Users of the class being mixed into should be able to access public members of either class through an instance of the containing class.
4. Multiple mixins can be applied to the same class (to the degree that their interfaces do not conflict). And separate mixins into the same type can effectively communicate with one another.
5. Mixin composition. That is, building a new mixin which is the sum of itself with a second mixin.
The rules of C++ do not allow a concrete class to be able to call functions it does not know about. And since we want mixins to be arbitrarily compose-able, a mixin cannot know what it is derived from. The obvious solution here is a template of some kind.
There are two general strategies for building mixins in C++. One relies on explicitly inverting the class hierarchy, while the other involves using a regular hierarchy and clever casting tricks. They each have their drawbacks.
### Hierarchy Inversion
In this case, mixins are implemented by having the mixin be the *derived* class. Each such mixin looks something like this:
template<typename Base>
struct mixin : public Base
{
//Add members here.
};
Users in this case do not use the `Base` class directly. They use the fully composed mixin type, which is built from a nested template sequence, usually hidden in an alias:
using MyClass = mixin1<mixin2<mixin3<Base>>>;
This allows mixins to have access to `Base` through a template interface. Concepts can be used to specify the particular interface needed.
The downsides of this approach are many. It does not fulfill #2, as the base class cannot directly communicate with any mixins that it uses. This is an acceptable limitation if the class's main functionality is built from mixins. But if a mixin is being used to extend the class's functionality, this hierarchy inversion becomes untenable.
It also makes it impossible to have constructs like private mixins, where the mixin provides some facility that is merely used by the `Base` class without being exposed to external code. Yet this it still needs to be a mixin, since that functionality requires the inverted relationship. So these mixins are only useful for public interfaces.
It also makes #3 difficult, as users must name the entire mixin chain when naming the type. Obviously a typedef makes this work just fine, but it does make the ultimate typename rather cumbersome. It also means that `mixin1<mixin2<Base>>` is a fundamentally different type from `mixin2<mixin1<Base>>`, despite being functionally identical.
On the plus side, it makes it easy for two mixins to communicate effectively. Granted, the communication can only proceed in one direction. But mixin composition works.
Another positive of this approach is that it's very simple and difficult to get wrong. It relies on normal inheritance relationships in C++. Inversion is achieved simply by deriving in the opposite direction.
### CRTP
The curiously reoccurring template pattern (CRTP) can be used for a more featureful mixin. The way this works is simple. The mixin is a template, which is passed the *derived* class as a template argument. Thus, the derived class inherits the interface of the mixin. But because the mixin knows its derived class type, it is able to perform a `static_cast` of its `this` pointer to that type. And thereby access the derived class's members.
Here is an example implementation of this:
template<typename derived>
class mixin
{
public:
void caller() const
{
auto _this = get_this();
_this->some_function();
}
private:
derived *get_this() {return static_cast<const derived *>(this);}
const derived *get_this() const { return static_cast<const derived *>(this); }
};
class destination : public mixin<destination>
{
public:
void some_function() {...}
};
This kind of mixin satisfies all of the requirements. There is full cross-talk; the derived class can access the mixin and vice-versa. Though they can only access the public members of each, that is an acceptable limitation. Users of the derived class can access the mixin's public members (assuming public inheritance) as though they were members of the derived class. And thanks to multiple inheritance, it is easy for one type to use multiple mixins. Indeed, mixins can be composed with one another.
The most obvious problem is the inconvenience. The need to convert the `this` pointer manually is a particular annoyance. But we don't necessarily add language features for simple convenience. So instead, let us look at where this pattern breaks down.
Mixin composition works by passing the derived class through to the base class. That is, if you have a mixin `A`, and you want to build a combined mixin `B` out of it, you do this:
template<typename derived>
class A {...};
template<typename derived>
class B : public A<derived> {...};
And therefore, if `B` implements part of the interface that `A` requires, it will pick up that interface through its access to `derived` (since `derived` is derived from `B`).
However, the inversion of control here is not truly inverted. If `derived` *also* implements the interface from `A`, then `derived`'s version is the one that `A` will call. This works as expected for inheritance: the most derived version wins.
That is not inverting the control relationship. If we wanted true inversion, then the least derived version should win. So if both `B` and `derived` implement a part of `A`'s interface, then `A` ought to call `B`, not `derived`.
There is no way to achieve this in C++ as it stands. Or at least, not without other consequences. If you tried this:
template<typename derived>
class B1 : public A<B1<derived>> {...};
Then `B1` is required to implement the *entire* interface that `A` expects. So if `B1` wants to pass some of that interface through, it must do so explicitly.
This particular form of derivation is useful, but it means something fundamentally different from `B`. `B1` is saying that it is using a mixin, but it is not *composing* itself from that mixin. That is, `B1` is a mixin that just so happens to be using a mixin in its implementation. Whereas `B` is intended to be full mixin composition: the interace `B` exposes is a combination of its own requirements and those of `A`.
The CRTP mixin implementation also has no way to check for mistakes. Here are some things you have to do with such mixins that are not (directly) checked for:
* Use the correct type when deriving from them. The compiler will catch the error in the implementation, but only because it's instantiating the template and can't find the interface it expects. So the compilation error will be far from the actual problem.
* Not use virtual inheritance. That breaks the `static_cast`. The compiler can't check for that one.
* Never declare objects of mixin types directly. You cannot write this declaration: `mixin<SomeType> a;`. The compiler won't catch that either; the mixin will assume it is derived from `SomeType`. And even if `SomeType` does derive from it, it also assumes that any base class instance is also a derived class instance. Obviously, this declaration violates that expectation, leading to odd runtime failures as `static_cast` goes awry.
So the CRTP implementation, while providing nearly all of the behavior we want, is somewhat fragile.
There are a few limitations that apply to both CRTP and hierarchy inversion.
It is impossible to use either method with anonymous classes. Both methods rely on the user being able to type the name of the class. This is not a particularly problematic limitation, since C++ does not even allow such classes to declare constructors/destructors.
A more burdensome problem has to do with the size of the resulting object. Many mixins have no NSDMs. One of the main purposes of mixins is to allow the contained object to provide the storage for some data, or simply to augment the interface of the contained type.
Despite having no data, such objects can impact the size of the resulting object. If the contained type was standard layout, then empty base optimization takes care of the issue. But if it was not (perhaps the containing object has virtual functions, or some other mixin brings in its own members), then compilers are free to not optimize empty bases. And therefore, you cannot rely on such optimizations. If class size is of importance to you, every mixin potentially could be making your class bigger.
Needlessly.
## Inner Class
The concept of Java-style inner classes needs some explanation, as it is very unlike normal C++ classes.
In C++, if you declare a class within another class definition (nested classes), the nested class has absolutely no relationship with the class that it is declared within. It is not even a friend of it, let alone a part of it. In C++, nested classes exist solely for scoping of the class's name.
In Java, a nested class is implicitly the friend of the class they nest. This allows nested Java classes to act as part of the class's interface. But a non-static nested class is even more special; it is an inner class.
Java inner classes implicitly allow their own `this` pointer to convert into a pointer to the instance that their current object instance is a member of. And since they are implicitly friends, they have direct access to all members of their containing instance. So if there is an instance `a` of a class `A`, and it has an inner class member `b`, then any non-static member function of `b` could call non-static member functions in `a`. Not just in the class, but in the *specific instance* `a` which holds this particular `b` instance object.
One can pass Java inner class members around to other functions, which can call functions on those inner class members. The members that get called can themselves still access `a`, even though the function that called them was passed `b`.
In terms of implementation, what is needed is a way to transform a pointer to `b` into a pointer to its owning object of type `A`. In Java, this is done by literally sticking a hidden pointer into the inner class type; garbage collection handles lifetime issues. This also allows Java inner classes to be declared outside of the class they are nested within. The code allocating it still has to have an object of that type, but the variable is not directly a part of the owning class.
Without generalized garbage collection, C++ can't really do that. As such, implementing inner classes in C++ is difficult. But this is not impossible. It does require quite a bit of help, though.
The general idea is to first restrict inner classes to being NSDMs of their containing instances. Then, have the containing class's constructors pass a `this` to every inner class member as part of those members' construction. Each inner class member stores a copy of that pointer. When an inner class member is being copied, care must be taken not to copy the `this` member.
These kinds of implementations are functional. But unlike mixins, it's a lot harder to ignore the difficulties of implementation. *Every* constructor of the owning class has a bit of boilerplate in it, making the whole process quite fragile. Every constructor of the inner class has to store the `this` pointer correctly. And because copying it cannot copy the `this` pointer, it cannot not be trivially copyable.
This also means that the class storing a member of the inner class type cannot be trivially copied either.
Composition of inner classes (inner classes of inner classes) is problematic. Each inner class gets to access its direct outer class, but they can access no outer classes beyond that without even more special coding. Though at least in this case, because inner classes are concrete classes, it is at least possible for the inner class to read the outer inner class's pointer to *its* outer class. But that requires that the user manually determine which function in which class in the inverted hierarchy needs to be called and then use the correct pointer to make that call.
Oh, and while empty base optimization is sometimes possible for mixins, there is no allowance in C++ for empty *member* optimizations. So every empty inner class member bloats the class. Though the fact that "empty" inner classes still need that `this` pointer means that they're not empty in *practice*, just in concept.
## Stateless Optimization
There are many uses of these inverted subobjects. However, quite a few of them involve the inverted class having no NSDMs at all. The state these objects manipulate or access is provided by the owning class, with the mixin/inner class merely providing an interface.
The problem is that, by the rules of C++, all members do take up space in the final class. While empty mixins are required to take up no space when used as bases of standard layout types, there is no such optimization for non-static data members. And even with base classes, if the owning class cannot be standard layout, then removing this space is entirely optional.
This issue has been one of the main reasons why people do not find some library implementations of properties attractive (implemented as a very limited form of inner classes). Making the interface slightly nicer to use is usually not worth the overhead. Therefore, finding a way to remove this overhead would be ideal, and would for many people be considered a necessary first step.
As such, this proposal includes not only the two inverted subobject types, but a third feature that both of the other two can make significant use of. This is the ability to declare that an empty class shall not take up room when declared as a subobject of another type.
# Design
Now that we have discussed what these features are, their limitations with current implementations, and what we could gain from putting them in the language, let's talk about how to add them into the C++ language. Here are the goals for the overall design of these features:
* Inverted and stateless classes should be restricted in functionality only where their design requires it, or to the degree which is implementable. For example, inner class instances are directly associated with an instance of their owning class. As such, it makes no sense to be able to declare automatic variables of them or put them on the heap, so restricting them to being declared as members of that class is acceptable. However, it should be perfectly reasonable to pass around references or pointers to such objects.
* If a class contains inverted subobjects, the class should be impacted by this as little as possible, except where obvious. In particular, we should try as much as possible to avoid complicating standard layout and trivial copy-ability rules, unless it is absolutely necessary to the implementation.
A word about syntax. This proposal effectively adds a number of new keywords to the language. This represents what I feel is the "perfect" syntax for these features, the best possible. However, this should be considered placeholder syntax, not the final form. For the time being, focus instead on whether we want the functionality and how well it is defined, not the syntax used to state it.
Overall, this document should be looked on as a start point and a rational, not the final word. There are many elements of this design which can be debated and adjusted.
## Stateless Classes
A stateless class is a class that is declared with the following syntax:
stateless class ClassName : BaseClasses
{
...
};
The `stateless` keyword must be associated with any forward declarations for stateless types.
class ClassName; //Not a stateless type.
stateless class ClassName //Compiler error: you told me it wasn't stateless before.
{
};
And vice-versa.
A stateless class may not:
* Have any NSDMs that are not themselves stateless classes.
* Have any base classes that are not themselves stateless classes.
* Have any virtual member functions.
* Have any virtual base classes.
The size of a stateless type is not modified by being stateless. That is, you will get what you would normally expect from an ordinary empty class.
The standard library should have an appropriate template metamethod (and variable) for testing if a type is stateless.
Stateless classes only have a special effect when declared as subobjects of another class type. When a stateless class is an NSDM subobject, it has no effect on the size or layout of that type. A stateless NSDM does not alter the layout of the class of which it is a member. A stateless non-virtual base class does not alter the storage or layout of the derived class. Thus, the layout/size of a type is only affected by its non-stateless/virtual subobjects.
Stateless object members otherwise have the same effects on their containing types as regular types do. For example, a stateless class can cause its owners to not be trivially copyable by itself having a non-trivial copy constructor (even though the type by definition has nothing to copy).
Template types may be stateless as well. However, individual specializations are allowed to declare themselves to change their stateless status. So you could declare that a `tuple` would be stateless iff all of its members are stateless.
Note that space optimization is only required when stateless classes are used as base class or NSDM subobjects. Stateless classes must take up memory when heap allocated (since they have a size). In other cases, compilers have the freedom to optimize their space or not, as they see fit. This is not required by this design because there is no user-visible behavior for where automatic variables are stored relative to one another.
Compilers would be allowed to assign the memory location of an automatic stateless variable to a stack location as normal, or to some arbitrary global value that all stateless variables get. Global/namespace/member-static/function-static variables work similarly. Any such stateless variables could all be assigned a single location.
In all other ways (except where discussed below), stateless types can be used exactly like regular types.
### Implementation and Restrictions
In a perfect world, the above would be the only restrictions on stateless types. C++ of course is never perfect.
In C++ as it currently stands, every object needs to have a memory location. And two unrelated types cannot have the *same* memory location.
Stateless types effectively have to be able to break this rule. The memory location of a stateless member subobject can be the same location as the object it is a member of. And two sibling members of the same type may have the same location if one of them is stateless.
This is not really an implementation problem, since stateless classes by their very nature have no state to access. As such, the specific nature of their `this` pointer is irrelevant. So we simply need a rule that effectively allows stateless types to be able to have the same memory location as any other type (whether stateless or not). This should not cause much implementation headache, beyond having to change the layout rules to account for objects with no size.
The behavior of the *user* converting pointers/references between two unrelated stateless types should still be undefined. We just need rules to allow stateless types to be assigned a memory location at the discretion of the compiler, which may not be unique from other unrelated objects.
Pointer arithmetic on arrays presents a bigger problem. In C++, the following should be perfectly valid for any type:
T t[5];
T *first = &t[0];
T *last = first + 5;
assert(sizeof(T) * 5 == last - first);
The whole point of a stateless type is that it does not take up space within another type. If the array above were a member of another type, this code should still work. But, since `sizeof(T)` is non-zero, that means that the member `t` takes up room in its containing type.
There are several ways to deal with this:
1. Declare that `sizeof` for stateless types is zero. I am not nearly familiar enough with the C++ standard to know the level of horrors that this would unleash, but I can imagine that it's very bad.
2. Declare that stateless types will only not take up space if they are direct subobjects of a class type. This is as opposed to being subobjects of arrays that themselves are subobjects of a class.
3. Forbid declaring arrays of stateless types altogether.
While #3 may seem a bit harsh, I think it is the best choice. For one, stateless types have no state, so declaring an array of them is pointless.
The downside of #3 is that it may impact template code, where the template does not know (or care) that a type is stateless. This would also be the first C++ construct that actively forbids such declarations on complete types. It would also forbid uses of stateless arrays in other contexts (stack variables, heap allocations), though primarily for orthogonality.
If the lack of orthogonality is too great, #2 can be adopted instead. The downside of #2 is that the statelessness of the class appears conditional: they don't take up room in other objects, except when they are arrayed. This also means that a stateless class must forbid NSDMs which are arrays of stateless classes.
## Inverted Class Members
Inverted classes of either kind can access their owning instance members from their member functions. But not from within *all* of their member functions.
Inverted class constructors and destructors explicitly do not have access to their owning class members. This is for the same reasons why virtual functions don't work in them. Member subobjects are constructed before their containing objects, so their owners don't exist yet. And member subobjects are destroyed after the destruction of their containing objects, so they would be accessing objects that have stopped existed.
But in all other cases, inverted class non-static member functions can access their owning class members.
## Inverted Class Scoping
The current design allows inner classes and mixins to access *everything* from their containers, with such access propagating through inner classes and mixins up to the first non-inner class/mixin type (or with a certain form of inheritance for mixins). This includes private members.
This seems like it breaks encapsulation a lot. However, I would argue that it does not truly do so.
Inner classes can only be declared within another class declaration. They can only be used as NSDMs of that class (or a one derived from it). Inner classes are as much a part of that class as one of its member functions. Thus, they should have the same rights as one of its member functions.
Mixins are a bit more troublesome in theory. A mixin is not contained within a class definition. So giving it private access makes it seem like users can gain access to a type whenever they want.
However, the mixin syntax proposed below requires that the mixin is added to the class at declaration time. And therefore, the implementer of the class is the one who decides which mixins to use and which not to use. So users are unable to inject a mixin into a class that does not deliberately choose to use it. Using a mixin is thus like declaring that a class is a friend: the implementer is still the one in control of who gets access.
If this is considered a deal-breaker, we can restrict mixins to only being able to access the public members of those they are mixed into. Alternatively, we can add syntax to specify what a particular mixin has access to. The class deriving from the mixin would probably be the best to determine this.
## Inner Classes
An inner class is a nested class that is declared as follows:
inner class Name
{
...
};
Inner classes may only be declared as nested classes (they may be nested within another inner class). The class an inner class is declared within is the direct owning class of that inner class.
As with normal class definitions, `Name` is optional (anonymous inner classes are quite useful). The `inner` keyword is part of the class's declaration, so if it is forward declared, it must be consistently declared with this keyword.
An inner class may or may not be stateless. The `stateless` keyword can go before or after the `inner` keyword.
An inner class can be derived from non-inner classes normally.
An inner class may be derived from another inner class, but only if the direct owning class of both is the same, or the direct owning class of the derived inner class is itself derived (non-virtually) from the direct owning class of the base inner class. From the derived inner class, it should be possible to go to its direct owner, then walk down the class hierarchy (non-virtually) to the class that is the direct owning class of the base inner class.
A non-inner class may not be derived from an inner class type.
Inner classes may have members and other definitions as befitting a regular class.
The standard library should have a template metafunction (and variable) for testing if a type is an inner class type.
### Type Usage
Variables of inner class types can only be declared as NSDM's of a class. And the class which they are declared within must be either:
* The direct owning class of the inner class.
* A class non-virtually inherited from the direct owning class of the inner class. The derived class in question must have access to the type (so private/protected inheritance can sever access, even without virtual inheritance).[^2]
Note that this means that the class which contains an instance of an inner class need not be exactly its owner. However, the container does need to be non-virtually derived from the owner.
Inner class types cannot be used as the type in `new` expressions.Temporaries cannot be created of inner class types. The only way to create an object of an inner class type is as an NSDM subobject of a type or a base class subobject of another inner class (as a consequence, it is never legal to explicitly call the destructor of an inner class).
Pointers and references to inner class types work as normal for any type.
Inner classes cannot be aggregated into arrays. The reason for this restriction [is discussed later](#inner_impl), but it is primarily due to implementation limitations.
How inner classes affect layout compatibility, trivial copyability, and various other aspects is very important. These decisions also factor heavily into how compilers can implement them. The following is the list of effects that are implementable when combined with the above restriction on inner class arrays. The [later discussion of implementation strategies](#inner_impl) allows for different usage restrictions and different effects.
The specific interactions with the containing class are:
* Inner class members can be copied/moved exactly as any other type. Specifically, if a stateful inner class type is trivially copyable, it should be legal to do a `memcpy(dest, src, sizeof(T))` from it to another instance of that type, even if the destination instance is in a different containing object instance.
* The fact that a particular NSDM is of an inner class type has no effect on [trivial copyability](http://en.cppreference.com/w/cpp/concept/TriviallyCopyable) with regard to the containing class. So an inner class member could only prevent the containing class from being trivially copyable if the inner class itself were not trivially copyable, not simply due to being an inner class.
* Types that declare stateful inner class members cannot have a trivial default constructor, and therefore cannot be [trivial types](http://en.cppreference.com/w/cpp/concept/TrivialType).
* Stateless inner class members do not affect the layout of the class containing them.
* Stateful inner class members prevent their owning types from being standard layout.
In all other ways, inner class types work just like regular types.
### Template Specialization
If an inner class is a template, then all of the specializations of that template must also be inner classes. And vice-versa. A template class's instantiation will therefore be either inner or not inner, regardless of the template parameters used on it.
The purpose of this rule is really user-sanity. Users probably don't mean for a specialization of some inner class template to not be an inner class. And vice-versa. Also, since the domain of inner class definitions is limited to within some other class, the potential for someone wanting to inject such a definition is low. So it's far more likely that this would happen due to user error than deliberate action.
### Containment Scope
Inner classes can be nested within inner classes. The reach of an inner class always extends up its ownership hierarchy to the first non-inner class. That first non-inner class is the boundary of the reach of the inner class. The classes between the inner class and the reaching class (including that class) are the inner class's scope.
An inner class is implicitly a friend of every class within their scope. Additionally, inner classes are implicitly friends with every class that is friends of one of the classes in its scope. Therefore, if a class in the inner class's scope has access to a name, so too does the inner class. This makes the inner class effectively part of the implementation of the eventual reached class.
Within member functions of inner classes (except for constructors and destructors), the following rules are added.
Members (static or not) of an inner class may access members of instances within its scope through a pointer/reference to its own class (including implicit `this`) as though it were derived from the classes in its scope. Name lookup takes place as though the direct owning class were placed at the end of the inner class's list of derived classes. Therefore explicitly derived classes take priority over owning ones. This implied derivation propagates up the entire scope of the inner class.
Qualified lookup within inner class member functions can also be performed through the scope as well. Similarly, inner classes may implicitly convert pointers/references of their own types into pointers/references to any class in their scope.
Friends of inner classes gain these abilities as well (mainly to allow lambdas to work), including the implied friendships.
Non-friends may perform explicit conversions from a pointer/reference to an inner class to a pointer/reference to one of its owning classes via `static_cast`. But implicit conversion is not allowed.
### Implementation {#inner_impl}
The fundamental operation of inner classes is the (static) transformation from a pointer to the inner class type to a pointer to its direct owning class type. Once such a conversion is possible for one step, it is possible for every step through an inner class's scope. This operation has to be implemented for two cases: one for stateless inner classes and one for non-stateless inner classes.
#### Stateless
The stateless version is based on the fact that pointers to stateless classes could point to anything. Therefore, they can point directly at their owning class instance. So the memory location of a stateless pointer should always be the same memory location of their owning class. If their owning class is itself a stateless inner class, then this propagates up the ownership hierarchy to the first non-stateless inner class or non-inner class.
Note that if the owner is a stateless class but not an inner class, then the memory location for that instance can be determined in the usual way for any other stateless class.
There is a snag that appears when a stateless inner class is an NSDM of a class derived from its owner. In this case, the compiler needs to recognize that its owning class is a base class of its container. This is a static property, so it is easy to know about. Once the compiler recognizes that, it can assign the pointer/reference of the NSDM by doing a `static_cast` from the derived class to the base class, then giving that memory location the inner class type.
This becomes more complex when deriving from inner classes:
class Base
{
public:
inner stateless struct In1
{ int GetBase() const {return baseVar;} };
private:
int baseVar;
};
class Derived : public Base;
{
public:
inner stateless struct In2 : public Base::In1
{
int GetBase2() const {return GetBase();}
};
In2 acc;
private:
int deriVar;
};
The key element to remember here is that both `Base` and `Derived` have non-stateless NSDMs, so they both take up space. `Derived` is not standard layout, and therefore the pointer to `Base` need not have the same memory location as a pointer to `Derived`.
But even with this complexity, the general plan still works. In `Derived::In2::GetBase2`, the compiler recognizes that `GetBase` is a member of the base class of `Derived::In2`. Therefore it needs to convert `this` from `Derived::In2*` to `Base::In1*`.
The compiler can also see that the base class is an inner class, whose owner is a base class of this inner class's owner.
So the sequence of conversion steps is simple. The compiler converts the current `this` pointer of type `Derived::In2*` into a `Derived*` (changing only the type), then converts it into a `Base*` (possibly changing the memory location), then converts it into an `Base::In1*` (again changing only the type). The same thing happens for calling `acc.GetBase()` directly.
All of these conversions are static, based on compile-time offsets. They do not rely on a particular instance of any of the stateless inner classes. In terms of performance, it is no different from doing `static_cast<Base*>(&derived)`.
#### Stateful
Stateful inner classes are more complex. This is because each stateful class instance must have a separate address from every other stateful NSDM. Because each member has its own state, the conversion from `Outer::Inner*` to `Outer*` is no longer a simple typecast or a static pointer offset. And if we want to be able to pass around references to stateful inner classes, we need to be able to, from the inner class's `this` pointer alone, convert it into a pointer to the owning class.
Therefore, for each stateful inner class member, the compiler must have access to some data (typically a byte offset) which is used to convert `this` from `Outer::Inner*` to `Outer*`. For multiple nestings of stateful inner classes, each level has its own offset to get to the next level.
The question is where this offset is stored.
One thing is certain: the offset must be stored someplace that is accessible from just a pointer to `this`. After all, users may have only a pointer/reference to an inner class member, and they have every reason to expect that this will be a working pointer/reference. In such a case, the only pieces of information the compiler has are the class definitions and `this`.
We are left with two alternative implementations. The offset could be stored within the inner class itself, in a hidden member (ala vtable pointers). Or the offset could be stored in the direct owning class, in memory that is directly adjacent to the instance.
Both of these have behavioral consequences. Both solutions break standard layout, as a consequence of having to insert additional data. But this is to be expected. The question is what functionality each option allows and what it forbids.
**Option 1: In-Object Memory:** This is the most obvious implementation strategy. The offset is simply a hidden field of the inner class.
The downside of this approach is that stateful inner classes cannot be trivially copied. This is for the same reason that virtual classes cannot be trivially copied. The value of the offset is based on the specific member variable and where it is within its owning type. As such, copying it between two *different* members (of the same type) is really bad. And since the offset is part of the type, you can't copy the two types with a simple `memcpy(dest, src, sizeof(T))`. And therefore, they are not trivially copyable.
The odd part here is that, while inner classes themselves are not trivially copyable, the class containing inner class members still can be trivially copied. This works because the data is an offset, not a pointer directly to the owner. As such, trivial copies can work if they are they are between the same variable (the variable defines the offset). Which they will be if the copy is between to instances of the containing class.
So this option does allow the containing object to remain trivially copyable. But the rules for trivial copyability become more complex, since the containing class does contain a member that is not trivially copyable.
The rules would have to say that inner classes are not trivially copyable. But if the only reason that the class is not trivially copyable is that it is an inner class, then using it as a base class or NSDM does not affect the trivial copyability of the containing class.
Also, the containing class cannot be *trivial*. For each stateful inner class NSDM, the default constructor must provide an offset, based on the layout of the class. Note that these offsets must exist for all non-copy/move constructors of the containing class.
**Option 2: Adjacent Memory:** The offset could be stored in the containing class as a hidden value. The offset would be stored directly before its associated inner class.
If the offset is adjacent to the object, then fetching the offset with only `this` is easy: simply decrement the pointer so that it points to the adjacent memory. The class definition will tell the compiler if it is stateful, and if it is, then it knows it needs to do this work to get the offset.
The adjacent memory method preserves trivial copyability of both the inner and containing classes, because the offset is where it needs to be: associated with the type that actually defines that offset. The offset is defined by the arrangement of the members of the outer class. So the outer class is the one that stores the offset. The outer class can still be trivially copied because the offset for each member is a static property, not a dynamic one.
The outer class cannot be *trivial* however, since its default constructor (and every other non-trivial-copy/move constructor) will need to initialize this hidden data.
The downside of this choice is that it breaks the ability to have arrays of stateful inner class members. The problem is that arrays in C++ are required to be contiguous, that the pointer distance from one array element to another must be exactly `sizeof(Class)`.
Note that if we forbid arrays of stateless classes period, forbidding arrays of state<b>ful</b> inner classes is not far afield.
The question is which is more useful: simpler trivial copyability rules and trivial copyability for inner classes vs. aggregating arrays of stateful inner classes? This proposal focuses on the former.
**Generating Offsets:** Stateful inner classes are complicated because inner classes can be used in two subobject forms: NSDMs and base classes.
The offset for the NSDM is simple: it is the byte offset from the pointer to that member to the owning class. Even if the NSDM is declared in a derived class of the owner, there is still some offset from that member to the owner.
The offset for base inner classes is more complex, as is where it gets computed. The byte offset needs to be an offset from the current `this` pointer to the owning instance. However, the current `this` pointer is not necessarily the NSDM's address; it's a base class subobject of that NSDM.
This offset must be generated by the containing class of the NSDM. So not only must it generate the offset for the NSDM inner class, it has to generate one for each inner class base of the NSDM.
### Other Implementation Based Restrictions
You will notice quite a lot of places where virtual inheritance is forbidden. This is done to ensure that all of the pointer conversions remain *static* operations. Virtual inheritance would require dynamic operations, where a simple pointer offset is not possible. Virtual functions themselves are fine.
Strict aliasing in C++ says that two unrelated pointers point to different objects. However, inner class members are related to one another, even if they are technically siblings. So the definition of "related" needs to be expanded to cover inner classes.
Specifically, inner class types are related to the class they are declared within. Recursively up to the first non-inner class.
## Mixin Classes
The syntax for mixin declarations is somewhat in question at present. The general ideas for the syntax is to:
1. Make being a mixin a first-class property of the type, which is associated with the type's declaration. This makes it possible for the compiler to verify that the user is using the mixin type correctly.
2. Rather than arbitrarily deciding which template parameter is the derived class type, allow the user to specify it explicitly. This makes the system easier for users to use, allowing the template argument interface to be what is most natural for the type.
3. Allow the directly derived class to not have to type its classname directly into the base class instantiation. This prevents user error, as well as making it abundantly clear to the reader that a mixin is being used.
4. Prevent users from accidentally putting the derived class type in the wrong location in the argument list. That is, both the derived class and the mixin implementation must agree which template argument is the mixin's owning type.
5. Allow the class including a mixin to determine if it is simply using the mixin or whether it is composing itself with the mixin.
### Class Declaration
A mixin class is a template class that is declared as follows:
template<parameters>
class name mixin<parameter>
{
...
};
The `parameter` associated with the `mixin` keyword must be the name of one of the `parameters` listed in the template argument list. This parameter must be a typename (it can be constrained by a concept). The specified template parameter becomes the owning type for the mixin.
The `parameter` may not be any form of expression; it must be exactly the name of a template parameter.
The `mixin` part of the class declaration must be a part of any forward declarations of the class. And it must consistently use the same parameter name (unless it is a template specialization).
A class that is declared as an inner class cannot also be a mixin.[^3]
Mixin classes obey all the rules of templates and classes, except where noted below.
The standard library should have a template metafunction (and variable) for testing if a type is an mixin class.
### Type Usage
There are two general ways that a mixin can be incorporated into a class. The class using it may want to be composed with the mixin, such that it forms a new mixin that is the union of itself and the mixin it is being composed with. Or the class may wish to simply use the mixin in its implementation.
To declare that a type is using a mixin, you derive from it with this syntax:
template<typename Derived>
class name mixin<Derived>
{
...
};
class derived : public name<mixin>
{
};
When performing mixin composition, the syntax changes to this:
template<typename Derived>
class name mixin<Derived>
{
...
};
template<typename Derived>
class composed mixin<derived> : public name<compose>
{
...
};
Note that compose syntax only works if the derived class is a mixin or an [inner class](#inverted_scope).
No variables of mixin types can be declared. Objects of mixin types can only be created as base subobjects of other classes, and they must use one of the two above syntaxes when deriving the class. Virtual inheritance of mixin classes is not allowed.
The `mixin` and `compose` keywords are used in the template instantiation syntax to refer to the specific template parameter that was specified to be the mixin type. The template will be instantiated as if `mixin`/`compose` was replaced by the derived class. If the mixin has multiple template parameters, the others can be filled in as normal. The `mixin`/`compose` keywords must be applied to the template parameter that was declared to be the mixin parameter in the template declaration.
Template substitution works exactly as if the user had used `name<derived>`. However, the system will note whether `mixin` or `compose` was used, as which one is used will have an effect on the [reach of the mixin](#mixin_scope).
Not using the `mixin`/`compose` keyword when deriving from a mixin class template is an error, even if it correctly names the derived type:
class mine : public name<mine> //Not allowed
{
};
Similarly, when deriving from non-mixin types, the use of the `mixin` or `compose` keyword in the template argument list is not allowed.
By using a special keyword to denote that a mixin is being used, we prevent several classes of errors (not passing the right type, etc). But we also allow the use of mixins on anonymous types:
//No name? No problem.
class : public_name<mixin>
{
} variable_name;
When naming the concrete mixin base class *within* the definition of members of the direct derived class, `mixin`/`compose` must be used in the template argument list. And the keyword used must match what was in the class definition. Outside of members of the derived class, the user must name the derived class explicitly.
Pointers and references to mixin classes can be used as normal. Mixin base classes operate as normal base classes do, except where mentioned below.
### Type Aliases and Mixins {#mixin_aliases}
Aliases for mixins work unusually. A mixin must always have a template parameter specified as the mixin's owning type. And aliases are no exception. Therefore, `typedef` cannot be used to create a mixin alias, only `using` syntax.
The `using` declaration must have at least one template parameter, and the syntax must specify which template parameter is the mixin's owning type. Furthermore, that parameter must be used in the alias itself. Here is an example:
template<typename T, typename derived>
class old_mixin mixin<derived>;
typedef old_mixin<int, some_class> bad1; //Illegal, no template parameter for the mixin type.
template<typename T>
using bad2 = old_mixin<T, some_class>; //Illegal, mixin type must refer to a template parameter.
template<typename derived>
using bad3 = old_mixin<int, derived>; //Illegal, new alias must specify which parameter is a mixin.
template<typename T, typename derived>
using bad4 mixin<T> = old_mixin<T, derived>; //Illegal, the specified mixin parameter is not passed to the original type's mixin parameter.
template<typename derived>
using correct mixin<derived> = old_mixin<int, derived>; //Works
Mixin template parameters can be constrained by concepts, but they must still resolve to a type.
The reason why mixin aliases must always have a template parameter for the mixin type is because of the syntax used for deriving from a mixin. The current syntax has the user put the `mixin`/`compose` keyword in the template argument list, which not only fills in the class but tells the compiler to make sure it's a mixin.
If we want to have cases like `bad1` or `bad2` work (those mixins could only be used with `some_class`), it is still important that the user deriving from the class make it clear that they are aware that it is a mixin. Because inheriting from a mixin [implicitly makes the mixin a friend of the class](#mixin_scope), it is very important to make it clear to the user at all times that a mixin is being derived from. We would also need a way to specify the particular form of inheritance: `mixin` vs. `compose`.
### Mixin Specializations
If one specialization of a template class is not a mixin, then all specializations must not be mixins.
Similarly, if one specialization is a mixin, all of them must be mixins. A mixin specialization may not declare a concrete class to be the mixin type. The parameter may be constrained via concepts, but it must still resolve to a type.
The reason for this limitation is similar to the [one for aliases](#mixin_aliases). If we allow specializations for a specific mixin base/derived class pairing, there would need to be some way for the user of the mixin to provide some idea that they intend to derive from that mixin.
### Inversion Scope {#mixin_scope}
Scoping for mixins is a bit more complex, due to the fact that the kind of composition affects how far a mixin can reach.
An instantiation of a mixin could reach through many classes that inherit from it. The class that directly derives from it is always reachable. However, a mixin's reach can only extend *through* that class if it used `compose` during its derivation. Therefore, a mixin can reach each derived class up its hierarchy to the first non-`compose` derivation. This first non-`compose` derivation is the mixin instantiation's reaching class.
The classes between the mixin class and its reaching class (including that class) are the mixin's scope.
Mixin instantiations are implicitly a friend of every class within their scope. Additionally, mixins are implicitly friends with every class that is explicitly friends of one of the classes in its scope.
Note that this rule *explicitly* excludes the implicit friendships from inner classes and other mixin derivation. The reason for excluding such friendships is to maintain some control over the mixin's reach. If we have a mixin that includes another mixin but does not use `compose`, we do not want the used mixin to be able to access anything beyond its reach. So the new mixin could have its own reach, while preventing the non-composed mixin from being able to have visibility through it.
Within member functions of mixin classes (except for constructors and destructors), the following rules are added.
Members (static or not) of a mixin class may access members of instances within its scope through a pointer/reference to its own class (including implicit this) as though it were derived from the classes in its scope. Name lookup takes place as though the owning class were placed at the end of the mixin class's list of derived classes. Therefore explicitly derived classes take priority over owning ones. This implied derivation propagates up the entire scope of the mixin.
Qualified lookup within mixin member functions can also be performed up the ownership hierarchy as well (this is not particularly useful, as the mixin only knows about its direct parent). Similarly, mixin classes may implicitly convert pointers/references of their own types into pointers/references to their owners, up through the mixin's scope.
Friends of mixins gain these abilities as well (mainly to allow lambdas to work).
The ability of non-friends to perform such conversions depends entirely on the access classes of those non-friends and of the inheritance diagram. Or to put it another way, the rules of casting up and down the hierarchy remain unchanged for non-friends of mixins.
### Possible Additions
* Given a mixin class, syntax could be added to get the derived class type. Perhaps `mixin<mixin_type>` would resolve to a typename. This would always be of the immediate parent class.
## Inverted Scope Interactions {#inverted_scope}
Inner classes and mixins propagate access and friendship up to the first non-inner class or non-`compose` mixin inheritance. But because they are really the same concept implemented in different ways, they should interact well together.
If a mixin declares an inner class, the inner class's reach extends through its mixin owner up to that class's eventual reach.
For the reverse, things are similar. When an inner class derives from a mixin, it may use `mixin` or `compose`. If it uses `compose`, then the reach of the mixin extends through the inner class to the eventual reach of that inner class. If it just uses `mixin`, the reach stops with the inner class.
Inner classes should always be considered part of the implementations of their owners. Mixins sometimes should and sometimes should not, depending on the class that is using it.
So if you have this:
template<typename derived>
struct M2 mixin<derived>
{
inner struct I : M1<compose>
{
int val_i;
} in;
int val_m2
}
struct T : public M2<mixin>
{
int val_t;
};
In instances of `T`, functions from `M2<T>::M1<I>` will be able to access `val_i`, `val_m2`, and `val_t`, in that order. If `I` had used `mixin` instead of `compose`, `M1<I>` would only be able to access `val_i`.
This feature allows users to use mixins to write the implementation of inner classes outside of the containing class. Thus the guts of an implementation can be used in multiple classes, while still allowing tclass-specific tweaks to be provided by the inner class using the mixin. For example, `I` could provide its own `val_m2` that shadows what `M1` can access. And because `M1`'s implementation is ignorant about the world outside of the mixin that it is declared within, it has no way to bypass this shadowing.
# Alternatives Not Taken
There are some alternative possible designs for these features. This will discuss why these are not being proposed.
## Full Java Inner Classes
This proposal is more narrowly focused than Java-style inner classes. In this proposal, inner class variables must be direct members of either their owning class or a class statically derived from it.
In Java, inner class variables can be allocated anywhere. And they can be attached to any instance of their owning class. Java even provides special `new` allocation syntax for doing so: `instance.new inner_class(...)`.
This is reasonable for Java because it is garbage collected. Therefore, an inner class can safely store a pointer to its owner. In C++, this is much less safe. While pointers/references to other objects have always been a part of C++, C++ also has various ownership relationships built into the type system. And any ability to create instances of inner classes as non-members would also need to allow the expression of that ownership. Would such an inner class take its owner as a `shared_ptr`?
Such functionality is simply too cumbersome for C++ as a language. It also lacks much of the utility of inner classes.
The restricted version of inner classes proposed here are sufficiently useful in their own right, and avoid pushing too far into dangerous and complex ownership issues.
## Mixin as a Separate Construct
Mixins, as presented here, are simply a small modification of the existing type system. They are just template classes, with all of the rights and restrictions thereof, except where it conflicts with the mixin design.
Other languages implement mixins as a separate thing, outside of the type system. D-style mixins, for example, are essentially a type-safe macro system. A type which uses a mixin effectively copies and pastes its definitions into another class.
The mixin system presented here is much more modest. Even so, it provides most of the functionality of a D-style mixin. The only downside of this version, relative to D-style mixins, is that it relies on C++'s multiple inheritance. Some people dislike multiple inheritance, both for objective and subjective reasons. But even this has advantages, as template functions can explicitly take pointers/references to classes which derive from a mixin, instead of using a concept-based interface.
# Remaining Issues to Resolve
* Arrays of stateless types: allowed or forbidden?
* Stateful inner class trivial copyability vs. declaring arrays of inner classes. Prioritizing the former will make the trivial copyability rules more regular, while prioritizing the latter provides more functionality and regularity. Particularly if stateless types can be arrayed.
* Pointers-to-member variables of stateless type. Is that even a problem? Could they not all have the same effective data?
* Mixin aliases & specializations. Currently, the mixin owning type must always be a template parameter, preventing complete mixin specializations for specific types. Should we allow full specialization?
* Full mixin inversion. The CRTP implementation of mixin composition works by passing the derived class through the mixin. This means that, if the composed mixin implements some part of the interface of one of its components, the derived class can still override that interface. However, the mixin language feature does not do this; it works based on checking the class hierarchy in reverse order: least derived first, then more derived. Which way is the way that users will expect it to work? Or do we need to explicitly allow the user to choose how it works per-mixin or per-derivation?
# Changes
* From version 0.5:
* Altered intro and restructured the document, putting uses after concepts.
* Removed erroneous claims about current CRTP implementations.
* Added functionality for explicitly restricting the reach of a mixin.
* Added discussion of mixin overriding inversion.
* Added section on alternative designs in other languages.
* Subsection on how inner class offsets are generated.
* Added section on changes.
[^1]: The term "mixin" as it is used by C++ may be different from how it is used in other languages. However, the concept is essentially the same: add features from outside of a class which work as if they had been declared within it as much as possible. Other languages might implement this concept as macros or via some explicit construct. C++ usually implements it through inheritance.
[^2]: Implementation note. For stateless inner classes in derived classes, the pointer to the stateless class has to have the same value as the base class. So special pointer gymnastics are needed when getting pointers/references to such variables. For stateful inner classes, the offset simply needs to be able to jump out of the derived class type and into the base class. So for some implementations, that may need to be a negative offset.
[^3]: This is not a strictly necessary restriction. Inner classes can be derived from other inner classes, after all. However, an inner class mixin could only ever be used as the base class of another mixin. And if you use `compose` when deriving that inner class, then the mixin can access the scope of the inner class deriving from it. So there is no need to declare the mixin to be an inner class. Also, it confuses questions of how the compiler converts the `this` pointer, whether it uses inner class conversion or mixin conversion. By forbidding them explicitly, we make it clear how conversion works for each step.
------=_Part_821_928106953.1446586761710--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Mon, 16 Nov 2015 15:11:35 -0500
Raw View
On 2015-10-29 14:04, Nicol Bolas wrote:
> So... here's a proposal that provides mixins, inner classes, and stateless
> classes to C++. What do you think? Is this a functional idea? What cases
> did I miss?
Sorry for jumping in so late... I'm still hoping to go back and re-read
the original proposal to compare notes. Meanwhile, finally read the
proposal. Comments follow:
> Let's say that you have some container class C. It has an interface
> that works for you and your needs. But with the ranges proposal, you
> know that it doesn't really fit into that paradigm. It is
> conceptually a range, but it doesn't actually fit the range paradigm.
> And you don't want to rewrite your interface; if it's not broken,
> don't fix it.
>
> The way to resolve that today would be to create some proxy object
> PC, which internally stores a pointer/reference to a C instance. This
> object would translate the interface of C into one appropriate for
> the ranges proposal.
Do you mean like
http://permalink.gmane.org/gmane.comp.lib.qt.devel/21327 (see
qtEnumerate)? Or http://eigen.tuxfamily.org/bz/show_bug.cgi?id=1104? :-)
Those sound like potential interesting use cases (if you happen to be
looking for concrete, real-world examples).
This brings up a related point; we'll want lifetime extension for inner
classes (i.e. retaining an inner class extends the life of a temporary
outer class that contains it).
IOW, I want to be able to do this:
Outer foo();
auto& i = foo().inner;
// ...do stuff with i
Or, equivalently, but more particularly:
for (auto i : foo().inner)
// ...
Hopefully though we'll be getting the general form of this and it can be
a moot point :-).
> In C++ as it currently stands, every object needs to have a memory
> location. And two unrelated types cannot have the same memory
> location.
Pedantic: I believe a struct and a member thereof can already have the
same address? The restriction applies to objects at the same scope.
Therefore, here:
> Stateless types effectively have to be able to break this rule. The
> memory location of a stateless member subobject can be the same
> location as the object it is a member of. And two sibling members of
> the same type may have the same location if one of them is
> stateless.
The first statement is already the case. Just the second one is new.
Is it worth talking about trivially copyability here? (Specifically,
stateless zero-sized classes should not affect trivial copyability.) Or
do you consider this sufficiently "obvious"?
> - Types that declare stateful inner class members cannot have a
> trivial default constructor, and therefore cannot be trivial types.
>
> - Stateful inner class members prevent their owning types from being
> standard layout.
There is some phrasing inversion going on here. I might consolidate
these as the first point: "...nor can such types be standard layout" (or
even just "...nor standard layout"). Or rephrase the first "Stateful
inner class members prevent their owning types from having a trivial
default constructor...".
> for each stateful inner class member, the compiler must have access
> to some data (typically a byte offset) which is used to convert this
> from Outer::Inner* to Outer*.
Question: is this necessary if a) there is exactly one NSDM of the
stateful inner class type, and b) no additional members of that type may
be added (e.g. by a derived class)?
I want to say "no", in which case we could theoretically allow these to
use a static offset also. *If* such an optimization is worthwhile, it
implies preventing additional NSDM's of the inner class type being
created. A private type would accomplish this, but I think also means
that no public member could use the type... so maybe it would be
interesting to allow e.g. "final" inner class types?
Just throwing it out there. I'm fine passing on it if it seems too
convoluted.
> Mixin Classes
> [...syntax...]
Is there a strong reason why a mixin can't just be an implicit template?
I.e.:
std::mixin class Foo { ... };
class Bar : public Foo { ... };
(This might require something like std::mixin_user to access the
typename of the final class explicitly, but that's easy.)
In case of additional template parameters, this would look like:
template <int N> std::mixin class Foo { ... };
class Bar : public Foo<3> { ... };
Naming a mixin (externally) could be done like:
typedef std::mixin<Bar> Foo<3> BarMixinFoo3;
Templated aliases would be conditionally legal:
template <int K>
using std::mixin<Bar> Foo<K>; // error
template <typename T>
using std::mixin<T> Foo<3>; // okay
template <int K> class Dog;
template <int K>
using std::mixin<Dog<K>> Foo<K>; // ???
The first is an error; the non-template-type Bar cannot have a templated
mixin. The second is always okay. The third is probably also legal, as a
declaration is not sufficient to know that Dog<K> always has mixin
Foo<K>. (Even the definition is insufficient, as specialization could
break the relation.) I believe you give a similar restriction? (I
honestly don't entirely follow that section.)
> No variables of mixin types can be declared
Just concrete instances? Or would this include pointers/references? I
believe the former is intended, but perhaps this could be worded to make
that more clear.
--
Matthew
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Mon, 16 Nov 2015 14:24:41 -0800 (PST)
Raw View
------=_Part_2600_1938000218.1447712681747
Content-Type: multipart/alternative;
boundary="----=_Part_2601_933077013.1447712681748"
------=_Part_2601_933077013.1447712681748
Content-Type: text/plain; charset=UTF-8
On Monday, November 16, 2015 at 3:11:50 PM UTC-5, Matthew Woehlke wrote:
>
> On 2015-10-29 14:04, Nicol Bolas wrote:
> > So... here's a proposal that provides mixins, inner classes, and
> stateless
> > classes to C++. What do you think? Is this a functional idea? What cases
> > did I miss?
>
> Sorry for jumping in so late... I'm still hoping to go back and re-read
> the original proposal to compare notes. Meanwhile, finally read the
> proposal. Comments follow:
>
> > Let's say that you have some container class C. It has an interface
> > that works for you and your needs. But with the ranges proposal, you
> > know that it doesn't really fit into that paradigm. It is
> > conceptually a range, but it doesn't actually fit the range paradigm.
> > And you don't want to rewrite your interface; if it's not broken,
> > don't fix it.
> >
> > The way to resolve that today would be to create some proxy object
> > PC, which internally stores a pointer/reference to a C instance. This
> > object would translate the interface of C into one appropriate for
> > the ranges proposal.
>
> Do you mean like
> http://permalink.gmane.org/gmane.comp.lib.qt.devel/21327 (see
> qtEnumerate)? Or http://eigen.tuxfamily.org/bz/show_bug.cgi?id=1104? :-)
> Those sound like potential interesting use cases (if you happen to be
> looking for concrete, real-world examples).
>
>
> This brings up a related point; we'll want lifetime extension for inner
> classes (i.e. retaining an inner class extends the life of a temporary
> outer class that contains it).
>
> IOW, I want to be able to do this:
>
> Outer foo();
> auto& i = foo().inner;
> // ...do stuff with i
>
> Or, equivalently, but more particularly:
>
> for (auto i : foo().inner)
> // ...
>
A member that is of an inner class type is still a non-static data member.
And they behave exactly like any NSDM, except where explicitly stated. So
if `foo().inner` would extend the lifetime if `inner` were an `int`, then
it would still extend the lifetime if `inner` were an inner class member.
> > In C++ as it currently stands, every object needs to have a memory
> > location. And two unrelated types cannot have the same memory
> > location.
>
> Pedantic: I believe a struct and a member thereof can already have the
> same address? The restriction applies to objects at the same scope.
> Therefore, here:
>
> > Stateless types effectively have to be able to break this rule. The
> > memory location of a stateless member subobject can be the same
> > location as the object it is a member of. And two sibling members of
> > the same type may have the same location if one of them is
> > stateless.
>
> The first statement is already the case. Just the second one is new.
>
> Is it worth talking about trivially copyability here? (Specifically,
> stateless zero-sized classes should not affect trivial copyability.) Or
> do you consider this sufficiently "obvious"?
>
Well, stateless classes can still affect trivial copyability just like any
other type. If you implement the copy constructor on them, then they are no
longer trivially copyable.
My point is that being declared as stateless has no effects on trivial
copyability one way or another. The rules work the same as they did before.
> for each stateful inner class member, the compiler must have access
> > to some data (typically a byte offset) which is used to convert this
> > from Outer::Inner* to Outer*.
>
> Question: is this necessary if a) there is exactly one NSDM of the
> stateful inner class type, and b) no additional members of that type may
> be added (e.g. by a derived class)?
>
> I want to say "no", in which case we could theoretically allow these to
> use a static offset also. *If* such an optimization is worthwhile, it
> implies preventing additional NSDM's of the inner class type being
> created. A private type would accomplish this, but I think also means
> that no public member could use the type... so maybe it would be
> interesting to allow e.g. "final" inner class types?
>
> Just throwing it out there. I'm fine passing on it if it seems too
> convoluted.
>
> > Mixin Classes
> > [...syntax...]
>
> Is there a strong reason why a mixin can't just be an implicit template?
>
I cordially dislike implicit templates. Also, with an explicit template, it
becomes much easier syntactically to create a contextual keyword.
How would you apply a concept restriction to an implicit template? How
would you name the class being mixed into at all?
Also, you may be looking at the old version of the proposal. The mixin part
in particular has been upgraded since then; my later post contains the
updated version.
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_2601_933077013.1447712681748
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Monday, November 16, 2015 at 3:11:50 PM UTC-5, Matthew Woehlke w=
rote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8e=
x;border-left: 1px #ccc solid;padding-left: 1ex;">On 2015-10-29 14:04, Nico=
l Bolas wrote:
<br>> So... here's a proposal that provides mixins, inner classes, a=
nd stateless=20
<br>> classes to C++. What do you think? Is this a functional idea? What=
cases=20
<br>> did I miss?
<br>
<br>Sorry for jumping in so late... I'm still hoping to go back and re-=
read
<br>the original proposal to compare notes. Meanwhile, finally read the
<br>proposal. Comments follow:
<br>
<br>> Let's say that you have some container class C. It has an inte=
rface
<br>> that works for you and your needs. But with the ranges proposal, y=
ou
<br>> know that it doesn't really fit into that paradigm. It is
<br>> conceptually a range, but it doesn't actually fit the range pa=
radigm.
<br>> And you don't want to rewrite your interface; if it's not =
broken,
<br>> don't fix it.
<br>>=20
<br>> The way to resolve that today would be to create some proxy object
<br>> PC, which internally stores a pointer/reference to a C instance. T=
his
<br>> object would translate the interface of C into one appropriate for
<br>> the ranges proposal.
<br>
<br>Do you mean like
<br><a href=3D"http://permalink.gmane.org/gmane.comp.lib.qt.devel/21327" ta=
rget=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D'http://www=
..google.com/url?q\75http%3A%2F%2Fpermalink.gmane.org%2Fgmane.comp.lib.qt.de=
vel%2F21327\46sa\75D\46sntz\0751\46usg\75AFQjCNHjJkplngmkVc97cq6jBeWxX7NA0A=
';return true;" onclick=3D"this.href=3D'http://www.google.com/url?q=
\75http%3A%2F%2Fpermalink.gmane.org%2Fgmane.comp.lib.qt.devel%2F21327\46sa\=
75D\46sntz\0751\46usg\75AFQjCNHjJkplngmkVc97cq6jBeWxX7NA0A';return true=
;">http://permalink.gmane.org/<wbr>gmane.comp.lib.qt.devel/21327</a> (see
<br>qtEnumerate)? Or <a href=3D"http://eigen.tuxfamily.org/bz/show_bug.cgi?=
id=3D1104" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D&#=
39;http://www.google.com/url?q\75http%3A%2F%2Feigen.tuxfamily.org%2Fbz%2Fsh=
ow_bug.cgi%3Fid%3D1104\46sa\75D\46sntz\0751\46usg\75AFQjCNG0oRW47teMHvHCmcF=
1SPfDA0e4nw';return true;" onclick=3D"this.href=3D'http://www.googl=
e.com/url?q\75http%3A%2F%2Feigen.tuxfamily.org%2Fbz%2Fshow_bug.cgi%3Fid%3D1=
104\46sa\75D\46sntz\0751\46usg\75AFQjCNG0oRW47teMHvHCmcF1SPfDA0e4nw';re=
turn true;">http://eigen.tuxfamily.org/bz/<wbr>show_bug.cgi?id=3D1104</a>? =
:-)
<br>Those sound like potential interesting use cases (if you happen to be
<br>looking for concrete, real-world examples).
<br>
<br></blockquote><div><br>=C2=A0</div><blockquote class=3D"gmail_quote" sty=
le=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left=
: 1ex;">This brings up a related point; we'll want lifetime extension f=
or inner
<br>classes (i.e. retaining an inner class extends the life of a temporary
<br>outer class that contains it).
<br>
<br>IOW, I want to be able to do this:
<br>
<br>=C2=A0 Outer foo();
<br>=C2=A0 auto& i =3D foo().inner;
<br>=C2=A0 // ...do stuff with i
<br>
<br>Or, equivalently, but more particularly:
<br>
<br>=C2=A0 for (auto i : foo().inner)
<br>=C2=A0 =C2=A0 // ...<br></blockquote><div><br>A member that is of an in=
ner class type is still a non-static data member. And they behave exactly l=
ike any NSDM, except where explicitly stated. So if `foo().inner` would ext=
end the lifetime if `inner` were an `int`, then it would still extend the l=
ifetime if `inner` were an inner class member.<br>=C2=A0</div><blockquote c=
lass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px=
#ccc solid;padding-left: 1ex;">
> In C++ as it currently stands, every object needs to have a memory=20
<br>> location. And two unrelated types cannot have the same memory
<br>> location.
<br>
<br>Pedantic: I believe a struct and a member thereof can already have the
<br>same address? The restriction applies to objects at the same scope.
<br>Therefore, here:
<br>
<br>> Stateless types effectively have to be able to break this rule. Th=
e
<br>> memory location of a stateless member subobject can be the same
<br>> location as the object it is a member of. And two sibling members =
of
<br>> the same type may have the same location if one of them is
<br>> stateless.
<br>
<br>The first statement is already the case. Just the second one is new.
<br>
<br>Is it worth talking about trivially copyability here? (Specifically,
<br>stateless zero-sized classes should not affect trivial copyability.) Or
<br>do you consider this sufficiently "obvious"?<br></blockquote>=
<div><br>Well, stateless classes can still affect trivial copyability just =
like any other type. If you implement the copy constructor on them, then th=
ey are no longer trivially copyable.<br><br>My point is that being declared=
as stateless has no effects on trivial copyability one way or another. The=
rules work the same as they did before.<br><br></div><blockquote class=3D"=
gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc so=
lid;padding-left: 1ex;">
> for each stateful inner class member, the compiler must have access
<br>> to some data (typically a byte offset) which is used to convert th=
is
<br>> from Outer::Inner* to Outer*.
<br>
<br>Question: is this necessary if a) there is exactly one NSDM of the
<br>stateful inner class type, and b) no additional members of that type ma=
y
<br>be added (e.g. by a derived class)?
<br>
<br>I want to say "no", in which case we could theoretically allo=
w these to
<br>use a static offset also. *If* such an optimization is worthwhile, it
<br>implies preventing additional NSDM's of the inner class type being
<br>created. A private type would accomplish this, but I think also means
<br>that no public member could use the type... so maybe it would be
<br>interesting to allow e.g. "final" inner class types?
<br>
<br>Just throwing it out there. I'm fine passing on it if it seems too
<br>convoluted.
<br>
<br>> Mixin Classes
<br>> [...syntax...]
<br>
<br>Is there a strong reason why a mixin can't just be an implicit temp=
late?<br></blockquote><div><br>I cordially dislike implicit templates. Also=
, with an explicit template, it becomes much easier syntactically to create=
a contextual keyword.<br><br>How would you apply a concept restriction to =
an implicit template? How would you name the class being mixed into at all?=
<br><br>Also, you may be looking at the old version of the proposal. The mi=
xin part in particular has been upgraded since then; my later post contains=
the updated version.</div><br>
<p></p>
-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_2601_933077013.1447712681748--
------=_Part_2600_1938000218.1447712681747--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 17 Nov 2015 10:28:46 -0500
Raw View
On 2015-11-16 17:24, Nicol Bolas wrote:
> On Monday, November 16, 2015 at 3:11:50 PM UTC-5, Matthew Woehlke wrote:
>> We'll want lifetime extension for inner classes (i.e. retaining an
>> inner class extends the life of a temporary outer class that
>> contains it).
>>
>> IOW, I want to be able to do this:
>>
>> Outer foo();
>> auto& i = foo().inner;
>> // ...do stuff with i
>>
>> Or, equivalently, but more particularly:
>>
>> for (auto i : foo().inner)
>> // ...
>
> A member that is of an inner class type is still a non-static data member.
> And they behave exactly like any NSDM, except where explicitly stated. So
> if `foo().inner` would extend the lifetime if `inner` were an `int`, then
> it would still extend the lifetime if `inner` were an inner class member.
Ack, true, true... let me rephrase that:
Foo::Inner& bar(Foo&);
auto& i = bar(foo());
// ...do stuff with i
I *want* to say this should work. However, I'm now thinking it would be
better addressed by the lifetime extension proposal. And per above,
hopefully the inner class proposal doesn't need to say anything about it
specifically as it would be handled under the same rules as any member
(per above).
>> On 2015-10-29 14:04, Nicol Bolas wrote:
>>> In C++ as it currently stands, every object needs to have a memory
>>> location. And two unrelated types cannot have the same memory
>>> location.
>>>
>>> Stateless types effectively have to be able to break this rule. The
>>> memory location of a stateless member subobject can be the same
>>> location as the object it is a member of. And two sibling members of
>>> the same type may have the same location if one of them is
>>> stateless.
>>
>> Is it worth talking about trivially copyability here? (Specifically,
>> stateless zero-sized classes should not affect trivial copyability.) Or
>> do you consider this sufficiently "obvious"?
>
> My point is that being declared as stateless has no effects on trivial
> copyability one way or another. The rules work the same as they did before.
Right; I agree with the statements made in the proposal. I'm only
mentioning, as a point for consideration, if *this specific spot* would
benefit from a brief mention of that (even just a reference to see some
other part of the proposal). That's all.
On further consideration... maybe not. It *is* covered later, after all,
and I'm less convinced that a mention here would actually be beneficial.
(I'll blame this partly on having written comments as I was reading
through.)
>>> Mixin Classes
>>> [...syntax...]
>>
>> Is there a strong reason why a mixin can't just be an implicit template?
>
> I cordially dislike implicit templates. Also, with an explicit template, it
> becomes much easier syntactically to create a contextual keyword.
Fair enough. Anyway, this is syntax, which isn't set in stone yet.
> How would you apply a concept restriction to an implicit template?
Hmm, that's a fair question.
> How would you name the class being mixed into at all?
(e.g.) mixin_parent.
> Also, you may be looking at the old version of the proposal. The mixin part
> in particular has been upgraded since then; my later post contains the
> updated version.
I did read both (at least, I skimmed the changes), but I also did
originally write comments against the old version.
Just to be clear, I like the proposal overall. Thanks again!
--
Matthew
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.