Jump to page: 1 2
Thread overview
Nested Structs
Dec 12, 2012
js.mdnq
Re: Nested Structs (Solution)
Dec 12, 2012
js.mdnq
Dec 12, 2012
js.mdnq
Dec 12, 2012
Max Samukha
Dec 13, 2012
js.mdnq
Dec 13, 2012
Max Samukha
Dec 13, 2012
js.mdnq
Dec 13, 2012
d coder
Dec 13, 2012
js.mdnq
Dec 13, 2012
Rob T
Dec 13, 2012
Mafi
Dec 14, 2012
js.mdnq
Dec 14, 2012
Rob T
Dec 14, 2012
js.mdnq
Dec 14, 2012
js.mdnq
Dec 14, 2012
Rob T
Dec 14, 2012
js.mdnq
Dec 16, 2012
Rob T
Dec 16, 2012
js.mdnq
December 12, 2012
The follow code demonstrates a solution to the nested structs. It is far from optimal but maybe it is possible to use as a base solution:

http://dpaste.dzfl.pl/7f086694

What the code demonstrates is that a nested struct can use the parent class(or struct) without having to store a reference to it. This, then, does not waste space to store a reference that is not needed.

The issue is we must hard code the offsets of the struct objects inside the class(something the compiler already knows). Any modification of the class will require changing the values.

In fact, the compiler could do all the dirty work behind the scenes pretty efficiently(all compile time offset computation) and not require a templated struct(which seems to slow down the compilation of the program significantly... unless my computer is acting up).

In any case, the code is a first step. The next being computing the offsets of the structs automatically which will give a useful method of solving the problem until, hopefully, something is implemented directly in the compiler.














Code:

module main;

import std.stdio;

class A {
	A a;

	struct B(int ofs)  {
		int Value;
		A Parent()
		{
			auto p = cast(void *)&this - ofs;
			return cast(A)(p);
		}		
	}

	B!(12) b1;
	B!(12 + 4) b2;
	string Name;

	this()
	{
		Name = "Class";
		a = this;

	}
}



int main(string[] argv)
{




	auto asize = A.classinfo.init.length;
	auto bsize = A.B!(0).sizeof;
	A a = new A();
	auto x = a.b1.Parent();
	auto y = a.b2.Parent();
	
December 12, 2012
Here is a solution I came up with that seems to work fine and does not require hard coding any values. Hence, it is useable. Unfortunately it looks clunky and is: (and it would be nice to speed up the the method call and possible code it in such a way that if D directly supports this in the future it will be easy to update)


http://dpaste.dzfl.pl/64025e0a


The idea is rather simple: We pass the offset of the struct object to itself as a template parameter. This allows the struct to calculate where it is at relative to the parent, which then allows it to access the members of the parent. This is quite easy to do and no issue. (except, of course, copying the struct will potentially invalidate it's parent)

The problem is actually calculating the offsets of these structs in the class(which are passed to the struct). Hard coding is a no go and using offsetof will not work because of forward referencing(you need to know the size of the type to find out the offsets

To do this, kinda hackish, but works, is we template the class containing the structs. This allows us to conditionally create the class. A!0, creates a class that we can then use to get the offsets. The forward references are no longer since we use A!0 which passes dummy offsets to the structs. A!x, for x > 0, then calculates the offsets of the structs using A!0 as a blueprint. A!0 has no offsetof so no forward referencing. A!1 uses offsetof for A!0, so, again, no forward referencing. Since passing offsets do not change the size we should expect A!1 to have the layout as A!0.

The "trick" here is:

static if (dummy == 0) B!(0) b1; else  B!(A.b1.offsetof) b1;


As you can see in the code, it's quite messy and ideally should look like a normal nested struct inside a class without any template parameters being used.

I'm hoping someone can come up with an elegant way to wrap this neatly into a package that can be used as any normal nested struct, or close.

Thanks...














Code:

module main;

import std.stdio;




class cAt(int dummy)
{
	alias cAt!0 A;
	struct B(int ofs)  {
		int Value;
		A Parent()
		{
			auto p = cast(void *)&this - ofs;
			return cast(A)(p);
		}		
	}

	static if (dummy == 0) B!(0) b1; else  B!(A.b1.offsetof) b1;
	static if (dummy == 0) B!(0) b2; else  B!(A.b2.offsetof) b2;

	string Name;

	this()
	{
		Name = "Class";
	}
}
alias cAt!1 A;

int main(string[] argv)
{
	

	auto asize = A.classinfo.init.length;
	auto bsize = A.B!(0).sizeof;
	A a = new A();
	auto x = a.b1.Parent();
	auto y = a.b2.Parent();
	
	auto s1 = A.init.b1.offsetof;
	auto s2 = A.init.b2.offsetof;

	auto a_ptr = cast(void*)a;
	auto x_ptr = cast(void*)x;
	auto y_ptr = cast(void*)y;

	assert(a_ptr == x_ptr);
	assert(x_ptr == y_ptr);

	getchar();
	return 0;
}
December 12, 2012
Also, I initially tried to do

	B!(A.b1.offsetof) b1;

a'la

http://forum.dlang.org/thread/mailman.2627.1355335532.5162.digitalmars-d@puremagic.com

but dmd 2.060 crashes, which is why I moved on to using a static if.
December 12, 2012
On Wednesday, 12 December 2012 at 22:19:54 UTC, js.mdnq wrote:
> Also, I initially tried to do
>
> 	B!(A.b1.offsetof) b1;
>
> a'la
>
> http://forum.dlang.org/thread/mailman.2627.1355335532.5162.digitalmars-d@puremagic.com
>
> but dmd 2.060 crashes, which is why I moved on to using a static if.

That's 'outer', yet another half-done D feature. It should be implemented for nested structs.
December 13, 2012
On Wednesday, 12 December 2012 at 22:58:47 UTC, Max Samukha wrote:
> On Wednesday, 12 December 2012 at 22:19:54 UTC, js.mdnq wrote:
>> Also, I initially tried to do
>>
>> 	B!(A.b1.offsetof) b1;
>>
>> a'la
>>
>> http://forum.dlang.org/thread/mailman.2627.1355335532.5162.digitalmars-d@puremagic.com
>>
>> but dmd 2.060 crashes, which is why I moved on to using a static if.
>
> That's 'outer', yet another half-done D feature. It should be implemented for nested structs.

Half done? Has it even been implemented at all?

In any case my method seems to provide a solution to the problem in the mean time. I have updated the code to work correct(had a small bug) and it uses an alias to make it easier to update in the future if D finally does support this construct in full.

http://dpaste.dzfl.pl/64025e0a

If outer is the appropriate keyword then Parent can easily be changed. Little would have to be done to the structs to make it work. In fact, the method should work if D ever decides to support such a feature with little change. Compile times could be increased though by removing the templating, but unfortunately that might require a lot of work. A mixin might simplify it.
December 13, 2012
On Thursday, 13 December 2012 at 00:02:01 UTC, js.mdnq wrote:
>
> Half done? Has it even been implemented at all?

http://dlang.org/class.html#nested

It is implemented for nested classes but not structs.

>
> In any case my method seems to provide a solution to the problem in the mean time. I have updated the code to work correct(had a small bug) and it uses an alias to make it easier to update in the future if D finally does support this construct in full.
>
> http://dpaste.dzfl.pl/64025e0a
>
> If outer is the appropriate keyword then Parent can easily be changed.

The name doesn't matter. I just wanted to annoy Walter again with yet another bit of evidence that completeness of a feature is important even if it doesn't seem to have an obvious use case. People will do what is logical and end up with hacks like that.

> Little would have to be done to the structs to make it work. In fact, the method should work if D ever decides to support such a feature with little change. Compile times could be increased though by removing the templating, but unfortunately that might require a lot of work. A mixin might simplify it.
December 13, 2012
On Thursday, 13 December 2012 at 00:37:16 UTC, Max Samukha wrote:
> On Thursday, 13 December 2012 at 00:02:01 UTC, js.mdnq wrote:
>>
>> Half done? Has it even been implemented at all?
>
> http://dlang.org/class.html#nested
>
> It is implemented for nested classes but not structs.
>
>>
>> In any case my method seems to provide a solution to the problem in the mean time. I have updated the code to work correct(had a small bug) and it uses an alias to make it easier to update in the future if D finally does support this construct in full.
>>
>> http://dpaste.dzfl.pl/64025e0a
>>
>> If outer is the appropriate keyword then Parent can easily be changed.
>
> The name doesn't matter. I just wanted to annoy Walter again with yet another bit of evidence that completeness of a feature is important even if it doesn't seem to have an obvious use case. People will do what is logical and end up with hacks like that.
>
>> Little would have to be done to the structs to make it work. In fact, the method should work if D ever decides to support such a feature with little change. Compile times could be increased though by removing the templating, but unfortunately that might require a lot of work. A mixin might simplify it.

Ok, maybe thats why my posts have not received any attention except by you ;)

Checking the sizes of classes, it seems that inner classes store a ptr to the outer class.

This is exactly what I am trying to avoid.

So, In fact, my method seems to save more space if I'm not mistake. Of course, they are not as safe since they don't keep the outer class ptr with them as they move around.

http://dpaste.dzfl.pl/b20e1412

It's strange that in my case the size of Q is 24 rather than 20 on dpaste.

In any case F should be 8 rather than 12 which suggests it is storing outer as a ptr inside the class.

When using my code, the struct size is 1 (the size of an empty struct) and the class size is 20. Not sure why a nested class increases the size of the parent class on my setup but not dpaste, but regardless, obviously D is storing a ptr in the classes, which are not necessary in many cases.

I think I have an idea how to make it a bit more elegant that I will try and if it works I'll start using it in my code.
December 13, 2012
> The name doesn't matter. I just wanted to annoy Walter again with yet
>> another bit of evidence that completeness of a feature is important even if it doesn't seem to have an obvious use case. People will do what is logical and end up with hacks like that.
>>
>
I believe it *is* an obvious use case. In D we use structs when we want to save on memory. And creating a class environment to encapsulate a system of structs is an obvious use case. In fact I am in this situation and thanks to your hack, I will try to use it for the time being.


>
>>  Little would have to be done to the structs to make it work. In fact,
>>> the method should work if D ever decides to support such a feature with little change. Compile times could be increased though by removing the templating, but unfortunately that might require a lot of work. A mixin might simplify it.
>>>
>>
> Ok, maybe thats why my posts have not received any attention except by you ;)


Let us trend it ;-)

Regards
- Puneet


December 13, 2012
On Thursday, 13 December 2012 at 01:57:26 UTC, d coder wrote:
>> The name doesn't matter. I just wanted to annoy Walter again with yet
>>> another bit of evidence that completeness of a feature is important even if
>>> it doesn't seem to have an obvious use case. People will do what is logical
>>> and end up with hacks like that.
>>>
>>
> I believe it *is* an obvious use case. In D we use structs when we want to
> save on memory. And creating a class environment to encapsulate a system of
> structs is an obvious use case. In fact I am in this situation and thanks
> to your hack, I will try to use it for the time being.
>
>
>>
>>>  Little would have to be done to the structs to make it work. In fact,
>>>> the method should work if D ever decides to support such a feature with
>>>> little change. Compile times could be increased though by removing the
>>>> templating, but unfortunately that might require a lot of work. A mixin
>>>> might simplify it.
>>>>
>>>
>> Ok, maybe thats why my posts have not received any attention except by you
>> ;)
>
>
> Let us trend it ;-)
>
> Regards
> - Puneet

I've created some mixins that make life a little easier:

http://dpaste.dzfl.pl/64025e0a

It makes it easier to refactor/rename.

mixin(StructNestType!("B", "_A!(", "_NestLevel", "b1"));

note the weird syntax of the class "_A!(", this is to allow when A has more than one template parameter.

Note that if you might want to create your constructors only for _NestLevel = true. e.g., use a static if. This way you don't have lame casting issues trying to cast one struct to another because they differ on the _NestLevel.

The opAssign is used to copy one type to another. One can only make assignments from objects of the same parent type. (else the parent will be wrong)

Using alias this will help avoid other problems. (which is sort of the point) These nested structs are meant to wrap values to add encapsulated functionality. As far as the outside world is concerned they are suppose to be just normal values, with, potentially, some additional functionality.

I believe that the code is now usable in that it accomplishes the task(zero overhead and small performance hit) and can be refactored somewhat easily. The syntax is far from pretty but if D ever gets such a feature it shouldn't be too hard to update the code(for large projects, one could make a simple regex parser for it to update everything).

It would be nice if D implements such a feature because it will look more natural.
December 13, 2012
On Thursday, 13 December 2012 at 08:55:44 UTC, js.mdnq wrote:
> It would be nice if D implements such a feature because it will look more natural.

It sure would be nice. With D, you should not have to mess around with a pointer like this.

Anyway, thanks for your efforts, we at least have a "hack" solution to try out.

BTW, there should be bug report or something about fixing the incomplete "outer" feature. Anyone looked yet?

--rt
« First   ‹ Prev
1 2