Thread overview
Why no acess to other structs in classes?
Nov 05, 2006
Karen Lanrap
Nov 05, 2006
John Demme
Nov 05, 2006
Bill Baxter
Nov 05, 2006
BCS
Nov 05, 2006
Bill Baxter
Nov 06, 2006
Bill Baxter
Nov 06, 2006
BCS
Nov 06, 2006
Mariano
Nov 06, 2006
Karen Lanrap
Nov 06, 2006
Bill Baxter
November 05, 2006
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(){}
November 05, 2006
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/
November 05, 2006
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
November 05, 2006
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(){}

November 05, 2006
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
November 06, 2006
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
November 06, 2006
== 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.
November 06, 2006
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
November 06, 2006
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?
November 06, 2006
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