digitalmars.D - Nested Structs
- js.mdnq (50/50) Dec 11 2012 The follow code demonstrates a solution to the nested structs. It
- js.mdnq (79/79) Dec 12 2012 Here is a solution I came up with that seems to work fine and
- js.mdnq (6/6) Dec 12 2012 Also, I initially tried to do
- Max Samukha (3/9) Dec 12 2012 That's 'outer', yet another half-done D feature. It should be
- js.mdnq (13/26) Dec 12 2012 Half done? Has it even been implemented at all?
- Max Samukha (7/21) Dec 12 2012 http://dlang.org/class.html#nested
- js.mdnq (21/47) Dec 12 2012 Ok, maybe thats why my posts have not received any attention
- Mafi (9/22) Dec 13 2012 It's not half-done. Structs are supposed to be POD. They should
- js.mdnq (116/141) Dec 13 2012 True nested structs do not contain hidden data. The data is
- Rob T (8/8) Dec 13 2012 I guess the complicating factor is that a nested struct could not
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();
Dec 11 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; }
Dec 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.
Dec 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.
Dec 12 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: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.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.
Dec 12 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.
Dec 12 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: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.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.
Dec 12 2012
The name doesn't matter. I just wanted to annoy Walter again with yetI 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.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.Let us trend it ;-) Regards - PuneetLittle would have to be done to the structs to make it work. In fact,Ok, maybe thats why my posts have not received any attention except by you ;)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.
Dec 12 2012
On Thursday, 13 December 2012 at 01:57:26 UTC, d coder wrote: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.The name doesn't matter. I just wanted to annoy Walter again with yetI 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.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.Let us trend it ;-) Regards - PuneetLittle would have to be done to the structs to make it work. In fact,Ok, maybe thats why my posts have not received any attention except by you ;)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.
Dec 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
Dec 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:It's not half-done. Structs are supposed to be POD. They should contain exactly the members that you list; no more hidden stuff. See TDPL 7.1.8 (p.262): "Unlike classes netsted within classes, nested structs and nested classes within structs dont contain any hidden member outer - there's no special code generated." I am not sure about classes within structs but nested structs should behave like they do IMHO. MafiAlso, 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.
Dec 13 2012
On Thursday, 13 December 2012 at 20:56:05 UTC, Mafi wrote:On Wednesday, 12 December 2012 at 22:58:47 UTC, Max Samukha wrote:True nested structs do not contain hidden data. The data is attached. There is no reason why a nested struct should not have access to it's outer class EXCEPT to prevent copy issues. If one is simply using structs to wrap types inside a class for encapsulation then it should be able to access the class members... else it becomes somewhat of an orphan without much use. Basically it's more of a matter of logical grouping. A class'es fields are laid out in memory sequentially. A struct field is just part of that layout. 01 Field 02 Field 03 Struct Field 03.1 Field 03.2 Field 04 Field etc... which can also be seen as 01 Field 02 Field 03 Field 04 Field 05 Field the struct field's this ptr is always some fixed offset into the class, so it is easy to get the outer by a simple calculation(as I have demonstrated with the code I posted). The usefulness, again, is that one can encapsulate(go from a class of nothing but fields to a class of structs) many pieces of functionality. But if the encapsulation prevents one from interaction with outer class members, then it can be too limiting. Nested classes solve this problem by storing a ptr to the class, that is ok, right? (it is necessary, my method won't work with classes since they are reference types) But if it's ok with classes then why are nested structs required to be orphans OR store a ptr to the class, which bloats the struct? The only thing "my" method does is stop one from having to store such a pointer to the class, which, for many cases, is redundant. So, if you want to argue against my method, really you should then be arguing against the case class A { struct B{ A a; } } because it is pretty much exactly the same except for one point: If we do away with the explicit ptr storage in the struct to save memory, we can't arbitrarily copy the nested structs to similar struct types. Alias this and opAssign help solve these problems though. I think my code solves a very real problem of providing efficient encapsulation of class data and functionality. One doesn't have to have one large huge class full of tons of fields and methods but can break a class down into chunks and build a hierarchy, similar to inheritance, but without the overhead. (a sort of slimed down inheritance. For my case, why I went down this pass, was to solve this problem: class A { byte X; .. other stuff... } Suppose I need to "intercept" assignments to X to, say, mask the value or whatever. How? I could create a new type. I could use an external struct. struct bbyte { byte x; alias x this; } class A { bbyte X; .. other stuff... } but suppose X is always associated with A, it's never found alone so to speak. Suppose, I need to access something from A while in X: class A { bbyte X; string Name; .. other stuff... } BUT, now bbyte is 4 times larger than it needs to be just to access A. Since I said X will always be part of A, we can do this: class A { struct bbyte { byte x; alias x this; A a; void Do() { writeln(a.Name); } } bbyte X; string Name; .. other stuff... } which is simply more logical but nothing different. BUT!!!! NOW! we can reduce the size of bbyte significantly: class A { struct bbyte { byte x; alias x this; void Do() { writeln(outer.Name); } } bbyte X; string Name; .. other stuff... } To me, this is a huge improvement. Of course, to do this in D one has to "hack" it up. As long as we don't create orphaned bbytes they always will have a parent. Since we are using them as "wrappers" we don't care all that much since any time they are used as "orphans" they are really just the value type they wrap. (through alias this and opAssign/cast) In some sense, these types are not value types but simply part of the class but have compiler like encapsulation. In any case, Just because everyone won't find it useful does not mean that it is not useful. Even if just a few people need some construct like this then it would be worthwhile to have. It really sucks when you need something and you don't have it.On Wednesday, 12 December 2012 at 22:19:54 UTC, js.mdnq wrote:It's not half-done. Structs are supposed to be POD. They should contain exactly the members that you list; no more hidden stuff. See TDPL 7.1.8 (p.262): "Unlike classes netsted within classes, nested structs and nested classes within structs dont contain any hidden member outer - there's no special code generated." I am not sure about classes within structs but nested structs should behave like they do IMHO. MafiAlso, 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.
Dec 13 2012
I guess the complicating factor is that a nested struct could not be copied out of one class into another of a different type, so I can see why it's not implemented. The compiler would have to prevent copies out, or the language would have to be modified to allow nesting but with some new convention to make it clear that the struct is nested, not sure if it's worth it though, you can always make do without it. --rt
Dec 13 2012
On Friday, 14 December 2012 at 06:27:39 UTC, Rob T wrote:I guess the complicating factor is that a nested struct could not be copied out of one class into another of a different type, so I can see why it's not implemented. The compiler would have to prevent copies out, or the language would have to be modified to allow nesting but with some new convention to make it clear that the struct is nested, not sure if it's worth it though, you can always make do without it. --rtThis is why I suggested a new type earlier so that it is obvious something is different. Since my initial reason for needed such a structure was to wrap built in types to provide functionality it's not a big deal because one can override opAssign and use alias this. Doing so basically lets the "outside world" use the type as if it were the value type and when copied they are copying the built in types rather than the structs. If it's a big deal it is not hard to create a duplicate struct with the added outer pointer similar to what is done with nested classes and then just copy over the data from one to the other. This will work fine except the need to create two structs with different, although, now that I think about it, I could potentially use a static if to create such a dual struct which would then help avoid orphans. When an assignment from the struct is used it will return, not itself, but either it's value(if a wrapper) or this new struct which is identical except containing a ptr to it's parent. I'll try to add it and see how it works.
Dec 13 2012
http://dpaste.dzfl.pl/64025e0a contains updated code. When the offset of the struct is 0 it contains an actual ptr to the class(the standard way) and hence can be "orphaned". When the offset is not 0 then it is part of a class object and can use a calculation to get the parent. Both methods work somewhat easily together as demonstrated by the last example. Unfortunately it seems we have to make an assignment. A.B!(0) b = a.b1; does not work but A.B!(0) b; b = a.b1; does. Not sure if there is some way to make the first work.
Dec 13 2012
On Friday, 14 December 2012 at 07:39:33 UTC, js.mdnq wrote:Unfortunately it seems we have to make an assignment. A.B!(0) b = a.b1;This one tries to call a constructor that takes in a typeof(a.b1). Do you have a constructor for it?does not work but A.B!(0) b; b = a.b1; does. Not sure if there is some way to make the first work.--rt
Dec 14 2012
On Friday, 14 December 2012 at 09:47:18 UTC, Rob T wrote:On Friday, 14 December 2012 at 07:39:33 UTC, js.mdnq wrote:oh, thats what it does?!?!?! ;) Ok, well, then it should be easy to add.Unfortunately it seems we have to make an assignment. A.B!(0) b = a.b1;This one tries to call a constructor that takes in a typeof(a.b1). Do you have a constructor for it?does not work but A.B!(0) b; b = a.b1; does. Not sure if there is some way to make the first work.--rt
Dec 14 2012
There's an interesting discussion going on that may be related to this subject. http://forum.dlang.org/thread/mailman.2705.1355596709.5162.digitalmars-d puremagic.com Note the definition with the "hidden reference frame" baggage, and to get rid of the extra baggage use "static struct". The reference frame mentioned however is not for a parent class or struct, but maybe its similar enough that nested structs could possibly be extended with a hidden reference to the parent class or struct. It all seems debatable though with various levels of complications associated with implementing a nest struct. --rt
Dec 15 2012
On Sunday, 16 December 2012 at 01:04:44 UTC, Rob T wrote:There's an interesting discussion going on that may be related to this subject. http://forum.dlang.org/thread/mailman.2705.1355596709.5162.digitalmars-d puremagic.com Note the definition with the "hidden reference frame" baggage, and to get rid of the extra baggage use "static struct". The reference frame mentioned however is not for a parent class or struct, but maybe its similar enough that nested structs could possibly be extended with a hidden reference to the parent class or struct. It all seems debatable though with various levels of complications associated with implementing a nest struct. --rtI was just looking at that and thought it was pretty interesting and that maybe the technique I used might apply. But after second thought I don't think it will work because copies of the struct are passed around instead. Which, if you recall, is still somewhat of an issue with the nested struct model I'm using. Of course, with overrides of opAssign, this, and opCast one could possibly avoid this issue too. It seems to me that the stack frame has taken place of the class object. When the voldemort struct is returned from the function, the outer values come from the stack frame(rather than the class object). When you return the voldemort struct, it also orphans it in a similar way it does for the class object. That is, unless the stack frame is always correct, which I haven't convinced myself of yet. Although I have a solution using the nested struct idea directly(simply use a functor), I think will work... I'll post it on the other thread. (this might imply that the voldemort construct is pretty solid, or can be made to be so)
Dec 16 2012