www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Problems with Array Assignment?

reply Samwise <mggmugginsmc gmail.com> writes:
I'm really sure this is just a stupid mistake I made, but I can't 
for the life of me figure out what is going on. Basically I'm 
trying to assign a reference to an object to an array, and the 
objects exist (an explicit destructor is writing lines at the end 
of the program, when the objects are GC'd), but there are not 
references to them in the array. You can see the complete code, 
and the line that I think is giving me trouble here: 
https://github.com/MggMuggins/TrafficLights/blob/master/source/renderer.d#L146

Any help is greatly appreciated. Like I said, I'm sure it's just 
a silly mistake because I don't understand something, but I 
appreciate any time you waste on me all the same. Thanks,
~Sam
May 10
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 10 May 2017 at 13:34:30 UTC, Samwise wrote:
 I'm really sure this is just a stupid mistake I made, but I 
 can't for the life of me figure out what is going on. Basically 
 I'm trying to assign a reference to an object to an array, and 
 the objects exist (an explicit destructor is writing lines at 
 the end of the program, when the objects are GC'd), but there 
 are not references to them in the array. You can see the 
 complete code, and the line that I think is giving me trouble 
 here: 
 https://github.com/MggMuggins/TrafficLights/blob/master/source/renderer.d#L146

 Any help is greatly appreciated. Like I said, I'm sure it's 
 just a silly mistake because I don't understand something, but 
 I appreciate any time you waste on me all the same. Thanks,
 ~Sam
tiles can be zero if you do not initialize it explicitly. assert(tiles, "tiles is zero"); should clue you in.
May 10
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
In your code, I see one big mistake:

---
class TileRenderer
{
private Tile[] tiles;
/*...*/
}

class CTileRenderer : TileRenderer
{
private CTile[] tiles;
/*...*/
}
---


Those are two separate arrays! Stuff examined through 
TileRenderer will be looking at a different array than looking 
through CTileRenderer.

I would just remove the `tiles` variable from the subclass and 
only use the base class one. I think that will work for you. 
writeTiles can do if(auto ctile = cast(CTile) tile) in the loop 
to get to the more specific class (or you might refactor your 
interfaces a bit. Really, I'd say `render` should just be a 
method in Tile, which its own subclasses can override.)
May 10
parent reply Samwise <mggmugginsmc gmail.com> writes:
On Wednesday, 10 May 2017 at 13:43:54 UTC, Adam D. Ruppe wrote:
 In your code, I see one big mistake:

 ---
 class TileRenderer
 {
 private Tile[] tiles;
 /*...*/
 }

 class CTileRenderer : TileRenderer
 {
 private CTile[] tiles;
 /*...*/
 }
 ---


 Those are two separate arrays! Stuff examined through 
 TileRenderer will be looking at a different array than looking 
 through CTileRenderer.
I wondered about that when I did it, but I assumed (wrongly) that since the name of the array was the same, it would override it. Thank you for suggesting that I make rendering a tile's responsibility instead of the renderer's. It honestly makes more sense that way, and my code is cleaner because of it. I did actually get it working because of that. Just to clarify, you can use a superclass variable to reference a member of a subclass? That seems to be what's happening. Thanks so much guys, I really appreciate it. My code it working now, and it's committed to github, so you can check it out if you'd like. Thanks! ~Sam
May 10
next sibling parent reply Samwise <mggmugginsmc gmail.com> writes:
I'm also having another problem with this program that is 
(sorta?) related. If you notice in my main (minus all the 
comments...), the code creates two tiles, then updates the 
renderer so that it renders those tiles. Then I create another 
tile in the same place as one of the first ones (not strictly 
relevant) and updates. Then the program pauses before it exits.

The expected behavior for this is to just see "Different Text" 
and "Other Text", instead of having any time to see just "Text". 
However, it seems that the second update method is having the 
effect that the first call should, and then the second is being 
called after the waiting.

Could anyone offer any insight on this? Again I'm sure it's an 
issue that I could have avoided had I known more when I wrote the 
code, but just don't yet. Thanks again for the help.
~Sam
May 10
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 May 2017 at 17:47:57 UTC, Samwise wrote:
 The expected behavior for this is to just see "Different Text" 
 and "Other Text", instead of having any time to see just 
 "Text". However, it seems that the second update method is 
 having the effect that the first call should, and then the 
 second is being called after the waiting.
My guess is just output buffering from consoled. Try `import std.stdio;` and `stdout.flush();` after each update and see what happens. I'm in a work meeting right now, i can look more after if this isn't it (and I will answer your other email too)
May 10
parent Samwise <mggmugginsmc gmail.com> writes:
On Wednesday, 10 May 2017 at 17:54:38 UTC, Adam D. Ruppe wrote:
 On Wednesday, 10 May 2017 at 17:47:57 UTC, Samwise wrote:
 The expected behavior for this is to just see "Different Text" 
 and "Other Text", instead of having any time to see just 
 "Text". However, it seems that the second update method is 
 having the effect that the first call should, and then the 
 second is being called after the waiting.
My guess is just output buffering from consoled. Try `import std.stdio;` and `stdout.flush();` after each update and see what happens. I'm in a work meeting right now, i can look more after if this isn't it (and I will answer your other email too)
Thanks a lot, that hit the nail on the head. Behaves exactly as I expected it to.
May 10
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 May 2017 at 17:26:09 UTC, Samwise wrote:
 I wondered about that when I did it, but I assumed (wrongly) 
 that since the name of the array was the same, it would 
 override it.
Nope, this is a somewhat common mistake coming from Python users, but it can't work that way in D since it would break static type guarantees - even if D did allow overriding variables, it would actually still flag your code as an error (try changing to a property getter, then you can override it, and you'll see it is an error). So what happens here is the child class just adds a new variable. You can access them individually through `yourvar.ClassName.tiles` - parent and child accessible but separate. In the child class though, `this` automatically binds to the most-local variable. A similar thing is this: class Foo { int a; this(int a) { a = a; // will compile but does nothing } } Inside that constructor, `a` refers to the local parameter, not the class member. So you'd have to write `this.a = a;` to assign it. (this is a FAQ too, so if you ever see a null that you could swear you initialized, this kind of mistake is usually the problem!) But same deal with child class members. Local ones can have the same name but are separate. Now, why would it be an error if it were allowed, like in the case of the property? It is because the base class can add ANY subclass of Tile, but the derived one assumes theya re all CTile: Derived derived = new Derived(); Base base = derived; // allowed, now goes through base class interface base.tiles ~= new OtherDerived(); // allowed, OtherDerived still comes from the same base class... CTile = derived.tiles[0]; // uh oh, it is actually OtherDerived, not CTile!!! In Python, since it is dynamically typed anyway, you don't have to worry about this as much, but in D since the base interface must still be usable, it would let you sidestep the child constraint entirely. I'm probably not explaining this well, but you can read about the same principle from Java sources like this Wikipedia link: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Arrays D works the same way for the same reasons.
 Just to clarify, you can use a superclass variable to reference 
 a member of a subclass? That seems to be what's happening.
Only if it is a virtual method. What's really happening here is that superclass and subclass have their own tiles arrays so you override the function but not the member and the data gets split up. tbh I kinda wish D would just call it an error to prevent the confusion.
May 10
parent Samwise <mggmugginsmc gmail.com> writes:
On Wednesday, 10 May 2017 at 20:01:16 UTC, Adam D. Ruppe wrote:
 ...
Thanks loads. Thanks to this my next commit is going to have quite a few improvements that clean up the engine code considerably. I appreciate the help.
May 10