Nested Structs (Solution)
js.mdnq
js_adddot+mdng at gmail.com
Thu Dec 13 16:07:25 PST 2012
On Thursday, 13 December 2012 at 20:56:05 UTC, Mafi wrote:
> On Wednesday, 12 December 2012 at 22:58:47 UTC, Max Samukha
> wrote:
>> On Wednesday, 12 December 2012 at 22:19:54 UTC, js.mdnq wrote:
>>> Also, I initially tried to do
>>>
>>> B!(A.b1.offsetof) b1;
>>>
>>> a'la
>>>
>>> http://forum.dlang.org/thread/mailman.2627.1355335532.5162.digitalmars-d@puremagic.com
>>>
>>> but dmd 2.060 crashes, which is why I moved on to using a
>>> static if.
>>
>> That's 'outer', yet another half-done D feature. It should be
>> implemented for nested structs.
>
> It's not half-done. Structs are supposed to be POD. They should
> contain exactly the members that you list; no more hidden stuff.
> See TDPL 7.1.8 (p.262): "Unlike classes netsted within classes,
> nested structs and nested classes within structs dont contain
> any
> hidden member outer - there's no special code generated."
> I am not sure about classes within structs but nested structs
> should behave like they do IMHO.
>
> Mafi
True nested structs do not contain hidden data. The data is
attached. There is no reason why a nested struct should not have
access to it's outer class EXCEPT to prevent copy issues. If one
is simply using structs to wrap types inside a class for
encapsulation then it should be able to access the class
members... else it becomes somewhat of an orphan without much use.
Basically it's more of a matter of logical grouping. A class'es
fields are laid out in memory sequentially. A struct field is
just part of that layout.
01 Field
02 Field
03 Struct Field
03.1 Field
03.2 Field
04 Field
etc...
which can also be seen as
01 Field
02 Field
03 Field
04 Field
05 Field
the struct field's this ptr is always some fixed offset into the
class, so it is easy to get the outer by a simple calculation(as
I have demonstrated with the code I posted).
The usefulness, again, is that one can encapsulate(go from a
class of nothing but fields to a class of structs) many pieces of
functionality. But if the encapsulation prevents one from
interaction with outer class members, then it can be too
limiting. Nested classes solve this problem by storing a ptr to
the class, that is ok, right? (it is necessary, my method won't
work with classes since they are reference types)
But if it's ok with classes then why are nested structs required
to be orphans OR store a ptr to the class, which bloats the
struct?
The only thing "my" method does is stop one from having to store
such a pointer to the class, which, for many cases, is redundant.
So, if you want to argue against my method, really you should
then be arguing against the case
class A
{
struct B{
A a;
}
}
because it is pretty much exactly the same except for one point:
If we do away with the explicit ptr storage in the struct to save
memory, we can't arbitrarily copy the nested structs to similar
struct types. Alias this and opAssign help solve these problems
though.
I think my code solves a very real problem of providing efficient
encapsulation of class data and functionality. One doesn't have
to have one large huge class full of tons of fields and methods
but can break a class down into chunks and build a hierarchy,
similar to inheritance, but without the overhead. (a sort of
slimed down inheritance.
For my case, why I went down this pass, was to solve this problem:
class A {
byte X;
.. other stuff...
}
Suppose I need to "intercept" assignments to X to, say, mask the
value or whatever. How? I could create a new type. I could use an
external struct.
struct bbyte { byte x; alias x this; }
class A {
bbyte X;
.. other stuff...
}
but suppose X is always associated with A, it's never found alone
so to speak. Suppose, I need to access something from A while in
X:
class A {
bbyte X;
string Name;
.. other stuff...
}
BUT, now bbyte is 4 times larger than it needs to be just to
access A. Since I said X will always be part of A, we can do this:
class A {
struct bbyte
{
byte x; alias x this;
A a;
void Do() { writeln(a.Name); }
}
bbyte X;
string Name;
.. other stuff...
}
which is simply more logical but nothing different.
BUT!!!! NOW! we can reduce the size of bbyte significantly:
class A {
struct bbyte
{
byte x; alias x this;
void Do() { writeln(outer.Name); }
}
bbyte X;
string Name;
.. other stuff...
}
To me, this is a huge improvement. Of course, to do this in D one
has to "hack" it up.
As long as we don't create orphaned bbytes they always will have
a parent. Since we are using them as "wrappers" we don't care all
that much since any time they are used as "orphans" they are
really just the value type they wrap. (through alias this and
opAssign/cast)
In some sense, these types are not value types but simply part of
the class but have compiler like encapsulation.
In any case, Just because everyone won't find it useful does not
mean that it is not useful. Even if just a few people need some
construct like this then it would be worthwhile to have. It
really sucks when you need something and you don't have it.
More information about the Digitalmars-d
mailing list