Thread overview
Accessing outer class attribute from inner struct
Aug 28, 2017
Andre Pany
Aug 28, 2017
Moritz Maxeiner
Aug 28, 2017
Andre Pany
Aug 28, 2017
Moritz Maxeiner
Aug 29, 2017
Andre Pany
Aug 29, 2017
Moritz Maxeiner
Aug 29, 2017
Andre Pany
August 28, 2017
Hi,

I build some framework to access Delphi components from D.
Delphi supports property array access "StringGrid1.Columns[2]" which is translated in Delphi to a private method call "GetColumn(2)".

I need to imitate this behavior in my D code.
Therefore my TCustomGrid class has a inner struct ColumnsArray with an opIndex.
While accessing opIndex I need to call a DLL method. Therefore I need the "reference" attribute which is available in my TCustomGrid via inheritance.

To make my question short:) If ColumnsArray is a class I can access the attribute "reference" but not if it is a struct. I would rather prefer a struct, but with a struct
it seems I cannot access "reference".

How can I access "reference" from my inner struct?

class TCustomGrid: TCustomPresentedScrollBox
{	
	struct ColumnsArray
	{
		TColumn opIndex(int index)
		{
			int r = getIntegerIndexedPropertyReference(reference, "Columns", index);
			return new TColumn(r);
		}
	}
	
	ColumnsArray Columns;
...
}

Kind regards
André
August 28, 2017
On Monday, 28 August 2017 at 21:52:58 UTC, Andre Pany wrote:
> [...]
>
> To make my question short:) If ColumnsArray is a class I can access the attribute "reference" but not if it is a struct. I would rather prefer a struct, but with a struct
> it seems I cannot access "reference".
>
> How can I access "reference" from my inner struct?
>
> [...]

Add an explicit class reference member to to it:
---
class TCustomGrid: TCustomPresentedScrollBox
{
	struct ColumnsArray
	{
		TCustomGrid parent;

		TColumn opIndex(int index)
		{
			int r = getIntegerIndexedPropertyReference(reference, "Columns", index);
			return new TColumn(r);
		}
	}
	
	ColumnsArray Columns;

	this()
	{
		Columns = ColumnsArray(this);
	}
...
}
---

Nesting structs inside anything other than functions[1] is for visibility/protection encapsulation and namespacing only.

[1] non-static structs in functions are special as they have access to the surrounding stack frame
August 28, 2017
On Monday, 28 August 2017 at 22:28:18 UTC, Moritz Maxeiner wrote:
> On Monday, 28 August 2017 at 21:52:58 UTC, Andre Pany wrote:
>> [...]
>>
>> To make my question short:) If ColumnsArray is a class I can access the attribute "reference" but not if it is a struct. I would rather prefer a struct, but with a struct
>> it seems I cannot access "reference".
>>
>> How can I access "reference" from my inner struct?
>>
>> [...]
>
> Add an explicit class reference member to to it:
> ---
> class TCustomGrid: TCustomPresentedScrollBox
> {
> 	struct ColumnsArray
> 	{
> 		TCustomGrid parent;
>
> 		TColumn opIndex(int index)
> 		{
> 			int r = getIntegerIndexedPropertyReference(reference, "Columns", index);
> 			return new TColumn(r);
> 		}
> 	}
> 	
> 	ColumnsArray Columns;
>
> 	this()
> 	{
> 		Columns = ColumnsArray(this);
> 	}
> ...
> }
> ---
>
> Nesting structs inside anything other than functions[1] is for visibility/protection encapsulation and namespacing only.
>
> [1] non-static structs in functions are special as they have access to the surrounding stack frame

Unfortunately thats not possible. ColumnsArray and the attribute will become a string mixin to avoid boilerplate.

It would be error prone if I have to initialize them in the constructor too. I want just 1 single coding line for this property. That is also the reason I do not want to use a class, as I would have to initialize them in the constructor.

Kind regards
André
August 28, 2017
On Monday, 28 August 2017 at 22:47:12 UTC, Andre Pany wrote:
> On Monday, 28 August 2017 at 22:28:18 UTC, Moritz Maxeiner wrote:
>> On Monday, 28 August 2017 at 21:52:58 UTC, Andre Pany wrote:
>>> [...]
>>>
>>> To make my question short:) If ColumnsArray is a class I can access the attribute "reference" but not if it is a struct. I would rather prefer a struct, but with a struct
>>> it seems I cannot access "reference".
>>>
>>> How can I access "reference" from my inner struct?
>>>
>>> [...]
>>
>> Add an explicit class reference member to to it:
>> ---
>> class TCustomGrid: TCustomPresentedScrollBox
>> {
>> 	struct ColumnsArray
>> 	{
>> 		TCustomGrid parent;
>>
>> 		TColumn opIndex(int index)
>> 		{
>> 			int r = getIntegerIndexedPropertyReference(reference, "Columns", index);
>> 			return new TColumn(r);
>> 		}
>> 	}
>> 	
>> 	ColumnsArray Columns;
>>
>> 	this()
>> 	{
>> 		Columns = ColumnsArray(this);
>> 	}
>> ...
>> }
>> ---
>>
>> Nesting structs inside anything other than functions[1] is for visibility/protection encapsulation and namespacing only.
>>
>> [1] non-static structs in functions are special as they have access to the surrounding stack frame
>
> Unfortunately thats not possible. ColumnsArray and the attribute will become a string mixin to avoid boilerplate.
>
> It would be error prone if I have to initialize them in the constructor too. I want just 1 single coding line for this property. That is also the reason I do not want to use a class, as I would have to initialize them in the constructor.

---
class C
{
    struct S
    {
    }
    S s;
}
---

is semantically equivalent to

---
struct S
{
}

class C
{
    S s;
}
---

with the two differences being
- namespacing (outside of C one has to use C.S to access S)
- you can protect the visibility of the S from outside the module C resides in via private,public, etc.

In both cases S doesn't inherently how about C, which means a solution using default initialization is not feasible, as S.init can't know about any particular instance of C.
I don't think there's any way for you to avoid using a class constructor.
August 29, 2017
On Monday, 28 August 2017 at 23:12:40 UTC, Moritz Maxeiner wrote:
>
> In both cases S doesn't inherently how about C, which means a solution using default initialization is not feasible, as S.init can't know about any particular instance of C.
> I don't think there's any way for you to avoid using a class constructor.

Thanks for the explanation. I now tried to use a class and use a static opIndex. But it seems from a static method you also cannot access the attributes of a outer class :)

class TCustomGrid: TCustomPresentedScrollBox
{	
	class ColumnsArray
	{
		static TColumn opIndex(int index)
		{
			// Reference is defined in TCustomGrid via inheritene
			int r = getIntegerIndexedPropertyReference(reference, "Columns", index);
			return new TColumn(r);
		}
	}
	
	alias Columns = ColumnsArray;
...
}

This seems like an unnecessary limitation...

Kind regards
André
August 29, 2017
On Tuesday, 29 August 2017 at 07:59:40 UTC, Andre Pany wrote:
> On Monday, 28 August 2017 at 23:12:40 UTC, Moritz Maxeiner wrote:
>>
>> In both cases S doesn't inherently how about C, which means a solution using default initialization is not feasible, as S.init can't know about any particular instance of C.
>> I don't think there's any way for you to avoid using a class constructor.
>
> Thanks for the explanation. I now tried to use a class and use a static opIndex. But it seems from a static method you also cannot access the attributes of a outer class :)

A nested class' outer property (when nested inside another class) is a class reference, which means we not only require a class instance of the outer class to reference, but also a class instance of the nested class to store said class reference to the other class in.
A static class method (by definition) is invoked without a class instance.
The two are inherently incompatible.

> [...]
>
> This seems like an unnecessary limitation...

I can only recommend reading the language specification w.r.t, nested classes [1] if it seems that way to you, because it is not.

[1] https://dlang.org/spec/class.html#nested

August 29, 2017
On Tuesday, 29 August 2017 at 08:30:24 UTC, Moritz Maxeiner wrote:
> On Tuesday, 29 August 2017 at 07:59:40 UTC, Andre Pany wrote:
>> On Monday, 28 August 2017 at 23:12:40 UTC, Moritz Maxeiner wrote:
>>>
>>> In both cases S doesn't inherently how about C, which means a solution using default initialization is not feasible, as S.init can't know about any particular instance of C.
>>> I don't think there's any way for you to avoid using a class constructor.
>>
>> Thanks for the explanation. I now tried to use a class and use a static opIndex. But it seems from a static method you also cannot access the attributes of a outer class :)
>
> A nested class' outer property (when nested inside another class) is a class reference, which means we not only require a class instance of the outer class to reference, but also a class instance of the nested class to store said class reference to the other class in.
> A static class method (by definition) is invoked without a class instance.
> The two are inherently incompatible.
>
>> [...]
>>
>> This seems like an unnecessary limitation...
>
> I can only recommend reading the language specification w.r.t, nested classes [1] if it seems that way to you, because it is not.
>
> [1] https://dlang.org/spec/class.html#nested

I think I found a solution which fulfills all goals but I have to try it out. A property method "Columns" will return an initialized struct "ColumnsArray" as proposed by you.
This should nicely work as string mixin. Thanks for your help.

Kind regards
André