Thread overview
Re: Visibility of variables in struct nested within a class
Jul 05, 2013
H. S. Teoh
Jul 05, 2013
Charles Hixson
Jul 05, 2013
John Colvin
Jul 05, 2013
John Colvin
Jul 05, 2013
H. S. Teoh
Jul 05, 2013
Charles Hixson
July 05, 2013
On Fri, Jul 05, 2013 at 11:19:53AM -0700, Charles Hixson wrote:
> I have a class that defines a variable (root).  Within it I have
> nested a struct.  If I refer to that variable within the struct I
> get the message:
> cbt2.d(760): Error: this for root needs to be type BTree not type Path
> 
> If I change the struct to a class, the error goes away, but I'd prefer to use a struct.
> 
> My computer is a 64bit Linux system with
> DMD64 D Compiler v2.063
> 
> The particular statement referenced is:
>             if    (root is null)
> though there are many others.
> 
> Is this the way things are supposed to happen?  (Changing the struct to a class is an OK patch, but I don't understand why it should either work or be necessary.)
[...]

Could you post some sample code that shows the problem? It's a bit hard to tell what's happening without seeing the actual code.


T

-- 
A bend in the road is not the end of the road unless you fail to make the turn. -- Brian White
July 05, 2013
On 07/05/2013 11:25 AM, H. S. Teoh wrote:
> On Fri, Jul 05, 2013 at 11:19:53AM -0700, Charles Hixson wrote:
>> I have a class that defines a variable (root).  Within it I have
>> nested a struct.  If I refer to that variable within the struct I
>> get the message:
>> cbt2.d(760): Error: this for root needs to be type BTree not type Path
>>
>> If I change the struct to a class, the error goes away, but I'd
>> prefer to use a struct.
>>
>> My computer is a 64bit Linux system with
>> DMD64 D Compiler v2.063
>>
>> The particular statement referenced is:
>>              if    (root is null)
>> though there are many others.
>>
>> Is this the way things are supposed to happen?  (Changing the struct
>> to a class is an OK patch, but I don't understand why it should
>> either work or be necessary.)
> [...]
>
> Could you post some sample code that shows the problem? It's a bit hard
> to tell what's happening without seeing the actual code.
>
>
> T
>


class    Outer
{    int    inn;

    struct    InnerS
    {    int    able;

        void    setable()
        {    able    =    inn;    }
    }    //    InnerS
}    //    Outer
void    main()
{

}

-- 
Charles Hixson

July 05, 2013
On Friday, 5 July 2013 at 20:47:45 UTC, Charles Hixson wrote:
I don't mean to come off as a formatting fascist, but you'll get more responses from busy people if you format in a more familiar way, e.g.

class Outer
{
    int inn;

    struct InnerS
    {
        int able;

        void setable()
        {
             able = inn;
        }
    }
}
July 05, 2013
On Friday, 5 July 2013 at 21:20:27 UTC, John Colvin wrote:
> On Friday, 5 July 2013 at 20:47:45 UTC, Charles Hixson wrote:
> I don't mean to come off as a formatting fascist, but you'll get more responses from busy people if you format in a more familiar way, e.g.
>
> class Outer
> {
>     int inn;
>
>     struct InnerS
>     {
>         int able;
>
>         void setable()
>         {
>              able = inn;
>         }
>     }
> }

woops, sorry, deleted the whole quote by accident.
July 05, 2013
On Fri, Jul 05, 2013 at 01:43:27PM -0700, Charles Hixson wrote:
> On 07/05/2013 11:25 AM, H. S. Teoh wrote:
> >On Fri, Jul 05, 2013 at 11:19:53AM -0700, Charles Hixson wrote:
> >>I have a class that defines a variable (root).  Within it I have
> >>nested a struct.  If I refer to that variable within the struct I
> >>get the message:
> >>cbt2.d(760): Error: this for root needs to be type BTree not type Path
> >>
> >>If I change the struct to a class, the error goes away, but I'd prefer to use a struct.
[...]
> class    Outer
> {    int    inn;
> 
>     struct    InnerS
>     {    int    able;
> 
>         void    setable()
>         {    able    =    inn;    }
>     }    //    InnerS
> }    //    Outer
[...]

The problem is that embedded structs don't have an implicit context pointer to the containing class, but an embedded class does. See:

	http://dlang.org/class.html

under "Nested Classes", toward the end.

When using a nested class, writing "able = inn" is actually shorthand for "this.able = this.outer.inn": this.outer is the implicit pointer that points to the outer class.

There may be a ticket in the bugtracker for supporting nested structs with .outer pointers, but AFAIK this currently isn't supported by the language.

One solution, if you prefer to use structs, is to implement the context pointer yourself:

	class Outer
	{
		int inn;
		struct InnerS
		{
			Outer* outer;
			int able;

			this(ref Outer _outer)
			{
				outer = &_outer;
			}
			void setable()
			{
				able = outer.inn;
			}
		}
	}

If you're 100% certain InnerS will never be used outside of class Outer, and it is only ever instantiated once inside the class, then you may be able to get away with using pointer arithmetic from the this pointer of InnerS to compute the reference to class Outer, but you'd be treading on tricky ground. Here's an example:

	class Outer
	{
		int inn;

		struct InnerS
		{
			int able;

			void setable()
			{
				// Buahaha
				int* innp = cast(int*)(cast(ubyte*)&this - Outer.inner.offsetof + Outer.inn.offsetof);

				// Behold! No need for context pointer!
				able = *innp;
			}
		}

		// Don't ever instantiate InnerS except here, otherwise
		// things will fail in ugly ways!
		InnerS inner;
	}

You could, of course, wrap up the ugly pointer arithmetic in a nice mixin, and pretty it up with parameters that allows you to use it generically for any struct nested in any class. But the above gives the basic idea behind it.

Note that pointer arithmetic is *not* recommended in general, as you will get memory corruption if you ever instantiate InnerS outside of Outer, or more than once in Outer, and then call setable(). But if you're desperate to avoid the overhead of an extra context pointer, the above may help you. :-P


T

-- 
"Outlook not so good." That magic 8-ball knows everything! I'll ask about Exchange Server next. -- (Stolen from the net)
July 05, 2013
On 07/05/2013 02:52 PM, H. S. Teoh wrote:
> On Fri, Jul 05, 2013 at 01:43:27PM -0700, Charles Hixson wrote:
>> On 07/05/2013 11:25 AM, H. S. Teoh wrote:
>>> On Fri, Jul 05, 2013 at 11:19:53AM -0700, Charles Hixson wrote:
>>>> I have a class that defines a variable (root).  Within it I have
>>>> nested a struct.  If I refer to that variable within the struct I
>>>> get the message:
>>>> cbt2.d(760): Error: this for root needs to be type BTree not type Path
>>>>
>>>> If I change the struct to a class, the error goes away, but I'd
>>>> prefer to use a struct.
> [...]
>> class    Outer
>> {    int    inn;
>>
>>      struct    InnerS
>>      {    int    able;
>>
>>          void    setable()
>>          {    able    =    inn;    }
>>      }    //    InnerS
>> }    //    Outer
> [...]
>
> The problem is that embedded structs don't have an implicit context
> pointer to the containing class, but an embedded class does. See:
>
> 	http://dlang.org/class.html
>
> under "Nested Classes", toward the end.
>
> When using a nested class, writing "able = inn" is actually shorthand
> for "this.able = this.outer.inn": this.outer is the implicit pointer
> that points to the outer class.
>
> There may be a ticket in the bugtracker for supporting nested structs
> with .outer pointers, but AFAIK this currently isn't supported by the
> language.
>
> One solution, if you prefer to use structs, is to implement the context
> pointer yourself:
>
> 	class Outer
> 	{
> 		int inn;
> 		struct InnerS
> 		{
> 			Outer* outer;
> 			int able;
>
> 			this(ref Outer _outer)
> 			{
> 				outer =&_outer;
> 			}
> 			void setable()
> 			{
> 				able = outer.inn;
> 			}
> 		}
> 	}
>
> If you're 100% certain InnerS will never be used outside of class Outer,
> and it is only ever instantiated once inside the class, then you may be
> able to get away with using pointer arithmetic from the this pointer of
> InnerS to compute the reference to class Outer, but you'd be treading on
> tricky ground. Here's an example:
>
> 	class Outer
> 	{
> 		int inn;
>
> 		struct InnerS
> 		{
> 			int able;
>
> 			void setable()
> 			{
> 				// Buahaha
> 				int* innp = cast(int*)(cast(ubyte*)&this - Outer.inner.offsetof + Outer.inn.offsetof);
>
> 				// Behold! No need for context pointer!
> 				able = *innp;
> 			}
> 		}
>
> 		// Don't ever instantiate InnerS except here, otherwise
> 		// things will fail in ugly ways!
> 		InnerS inner;
> 	}
>
> You could, of course, wrap up the ugly pointer arithmetic in a nice
> mixin, and pretty it up with parameters that allows you to use it
> generically for any struct nested in any class. But the above gives the
> basic idea behind it.
>
> Note that pointer arithmetic is *not* recommended in general, as you
> will get memory corruption if you ever instantiate InnerS outside of
> Outer, or more than once in Outer, and then call setable(). But if
> you're desperate to avoid the overhead of an extra context pointer, the
> above may help you. :-P
>
>
> T
>
Well, InnerS should *NEVER* be instantiated outside of Outer.  To ensure that every method and variable is private.  OTOH, if I should ever decide to implement ranges, I'll need more than one copy.  The "this" constructor with a parameter is a bit better, or I may just decide that simplicity is worth adding a huge lot of extra indirections, and leave it as a class.

This is the kind of thing where any manually maintained change is likely to be brittle, so I don't think that pointer arithmetic is at all reasonable.

So only the two choices of passing the containing class in the constructor and converting it into a class seem reasonable.

Actually, there's a third choice that is probably the one I'll use.  I didn't need the struct until I decided to refactor the code to make it simpler.  I can just undo the refactoring.  That's an ugly answer, but it doesn't sacrifice efficiency.  But first I'll read up on initializing struct.s with parameters.  I should understand that more anyway.

Looking over the original code a bit more, I don't think the constructor would work, as the tests for error happen during the calls on Inner methods.  So it's either class, or undo the refactoring.  And for this reason and that, probably that means undo the refactoring.  (It's not entirely for reasons of speed.  I'm also considering maintainability.)  Making it into a class rather than a struct *would* work, but I chose a struct because I wanted isolation (for simplicity) without a run-time penalty.  But it appears as if it wouldn't work, barring fragile tricks.

-- 
Charles Hixson