Visibility of variables in struct nested within a class

H. S. Teoh hsteoh at quickfur.ath.cx
Fri Jul 5 14:52:29 PDT 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)


More information about the Digitalmars-d-learn mailing list