www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why no acess to other structs in classes?

reply Karen Lanrap <karen digitaldaemon.com> writes:
class C
{
    struct S
    {
        uint data;
    }
    S s;
    struct T
    {
        uint f()
        {
            return s.data;
            // this for s needs to be type C not type T *
        }
    }
}
void main(){}
Nov 05 2006
next sibling parent reply John Demme <me teqdruid.com> writes:
Karen Lanrap wrote:

 class C
 {
     struct S
     {
         uint data;
     }
     S s;
     struct T
     {
         uint f()
         {
             return s.data;
             // this for s needs to be type C not type T *
         }
     }
 }
 void main(){}

Actually, it's more generic than that- the inner struct can't access any member variable of the outer class: class C { uint s; struct T { uint f() { return s; // this for s needs to be type C not type T * } } } void main(){} I'm not certain if this is a bug or if there is a good reason for this. -- ~John Demme me teqdruid.com http://www.teqdruid.com/
Nov 05 2006
parent Bill Baxter <wbaxter gmail.com> writes:
John Demme wrote:
 Karen Lanrap wrote:
 
 
class C
{
    struct S
    {
        uint data;
    }
    S s;
    struct T
    {
        uint f()
        {
            return s.data;
            // this for s needs to be type C not type T *
        }
    }
}
void main(){}

Actually, it's more generic than that- the inner struct can't access any member variable of the outer class: class C { uint s; struct T { uint f() { return s; // this for s needs to be type C not type T * } } } void main(){} I'm not certain if this is a bug or if there is a good reason for this.

Odd. I could have sworn I saw there was a .outer property added in a recent version. Apparently it only applies to inner classes, not structs. This compiles: class C { uint s; class T { uint f() { return this.outer.s; } } } void main(){} --bb
Nov 05 2006
prev sibling next sibling parent reply BCS <nada pathlink.com> writes:
Accessing the outer class would require a pointer to it. The only way to get
that
pointer would be to add a hidden value to the struct. In D, structs are bare
bones
aggregates without any hidden stuff added in.

== Quote from Karen Lanrap (karen digitaldaemon.com)'s article
 class C
 {
     struct S
     {
         uint data;
     }
     S s;
     struct T
     {
         uint f()
         {
             return s.data;
             // this for s needs to be type C not type T *
         }
     }
 }
 void main(){}

Nov 05 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
BCS wrote:
 Accessing the outer class would require a pointer to it. The only way to get
that
 pointer would be to add a hidden value to the struct. In D, structs are bare
bones
 aggregates without any hidden stuff added in.
 
 == Quote from Karen Lanrap (karen digitaldaemon.com)'s article
 class C
 {
     struct S
     {
         uint data;
     }
     S s;
     struct T
     {
         uint f()
         {
             return s.data;
             // this for s needs to be type C not type T *
         }
     }
 }
 void main(){}


I don't think so. Structs are always treated like plain old data. So in memory this: class C { int a; int b; } has exactly the same layout as class C { int a; struct S { int b; } S s; } Therefore, if it is possible for S to compute the address to it's own members: class C { int a; struct S { int b; int f() { return b+1; } } S s; } Then it should also be possible for S to compute the address of members in the outer class at compile time. Try running this program: ------------ import std.stdio : writefln; class C { int a; struct S { int b; int outer_a() { return cast(int)* (cast(char*)(&b)-int.sizeof); } } S s; } void main() { C c = new C; c.a = 42; writefln("c.a's value is ", c.s.outer_a()); } --------------- The offset to the members of the outer class (or outer struct) are compile time constants. It's well within the compiler's ability to compute them. --bb
Nov 05 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Bill Baxter wrote:
 BCS wrote:
 Accessing the outer class would require a pointer to it. The only way 
 to get that
 pointer would be to add a hidden value to the struct. In D, structs 
 are bare bones
 aggregates without any hidden stuff added in.

 == Quote from Karen Lanrap (karen digitaldaemon.com)'s article
 class C
 {
     struct S
     {
         uint data;
     }
     S s;
     struct T
     {
         uint f()
         {
             return s.data;
             // this for s needs to be type C not type T *
         }
     }
 }
 void main(){}


I don't think so. Structs are always treated like plain old data. So in memory this: class C { int a; int b; } has exactly the same layout as class C { int a; struct S { int b; } S s; } Therefore, if it is possible for S to compute the address to it's own members: class C { int a; struct S { int b; int f() { return b+1; } } S s; } Then it should also be possible for S to compute the address of members in the outer class at compile time. Try running this program: ------------ import std.stdio : writefln; class C { int a; struct S { int b; int outer_a() { return cast(int)* (cast(char*)(&b)-int.sizeof); } } S s; } void main() { C c = new C; c.a = 42; writefln("c.a's value is ", c.s.outer_a()); } --------------- The offset to the members of the outer class (or outer struct) are compile time constants. It's well within the compiler's ability to compute them. --bb

Ok ok. I was wrong. It is a little more complicated than that. If you have two instances of S inside the class, then you'd need two instances of the method outer_a, each with it's own compile time offsets. But then something like outer_a() { static int num_calls++; ... } would fail. One solution would be to add the hidden pointer-to-outer member to any structs that use the "outer" property. If you don't use it, you don't pay for it. Rather like how virtual functions are handled in C++. If you don't declare any methods to be virtual, then the compiler doesn't generate a vtable. In the mean time, the obvious workaround is to give the structs a pointer to the outer class yourself. class C { int a; this() { s.outer = this; s2.outer = this; } struct S { int b; int whats_A() { this.b += 1; return outer.a; } C outer; } S s; S s2; } --bb
Nov 05 2006
parent BCS <BCS pathlink.com> writes:
Bill Baxter wrote:
 Bill Baxter wrote:
 

 Ok ok.  I was wrong.  It is a little more complicated than that.  If you 
 have two instances of S inside the class, then you'd need two instances 
 of the method outer_a, each with it's own compile time offsets.  But 
 then something like
 
    outer_a() {
       static int num_calls++;
       ...
    }
 
 would fail

another case where the constant offset assumption will fail class C { int a; struct S { int b; int f() { return b+1; } int outer_a() { return cast(int)* (cast(char*)(&b)-int.sizeof); } /*static*/ void other() { S foo; foo.outer_a(); S* bar; bar.outer_a(); } }
 
 One solution would be to add the hidden pointer-to-outer member to any 
 structs that use the "outer" property.  If you don't use it, you don't 
 pay for it.  Rather like how virtual functions are handled in C++.  If 
 you don't declare any methods to be virtual, then the compiler doesn't 
 generate a vtable.
 

Ouch, I am a big fan of the "struct are just data" approach. I would rather, on the rare occasion this would be used, give up a bit of speed and go with objects than add hidden overhead to structs.
 In the mean time, the obvious workaround is to give the structs a 
 pointer to the outer class yourself.
 
 class C
 {
     int a;
     this() {
         s.outer = this;
         s2.outer = this;
     }
     struct S {
         int b;
         int whats_A() {
             this.b += 1;
             return outer.a;
         }
         C outer;
     }
     S s;
     S s2;
 }
 

That would also be a good solution
Nov 06 2006
prev sibling parent reply Mariano <rotoshi yahoo.com> writes:
== Quote from Karen Lanrap (karen digitaldaemon.com)'s article
 class C
 {
     struct S
     {
         uint data;
     }
     S s;
     struct T
     {
         uint f()
         {
             return s.data;
             // this for s needs to be type C not type T *
         }
     }
 }
 void main(){}

This seams like really bad practice. A function inside a struct returning something from outside itself? M.
Nov 06 2006
parent reply Karen Lanrap <karen digitaldaemon.com> writes:
Mariano wrote:

 This seams like really bad practice. A function inside a struct
 returning something from outside itself?

If it is bad practice, then why it is supported on the module level?
Nov 06 2006
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Karen Lanrap wrote:
 Mariano wrote:
 
 This seams like really bad practice. A function inside a struct
 returning something from outside itself?

If it is bad practice, then why it is supported on the module level?

It's also exactly the kind of thing iterators are going to have to do. class C { class MyIterator { int get() { return .outer.array[i]; } ... size_t i=0; } int[] array; } --bb
Nov 06 2006