www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Pointers or copies?

reply Orgoton <orgoton mindless.com> writes:
I'm making a game and I have to keep track of the objects in the game, which
inherit from a master object class called "objct". So I would make a class for
"baddie", "player" and such. Now, to draw them I have to keep track of their
existance, so, when one is created, it adds itself to the "frame queue" and
removes itself when it is deleted. To keep track of them I created a "queue"
class, which has a protected member called "objct *pters[]". It is an array of
pointers so when I recieve a new actor for the queue, I increase the array
size accordingly and on the next slot, just add it there. Simple. Now, the
queue class has a method called "add" and "remove" both of them take as
parameter "objct *target".

To sum it up and finally tell you my question the objct has in it's
constructor "framequeue.add(this);" and a correspondind "remove(this)".

Apparently, "this" is not a pointer... as the compiler claims: "function
queue.remoce (objct *target) does not match parameter types (objct)" and that
"cannot implicitly convert expression (this) of type objct to objct*". So,
does passing "this" create a copy of the object? How do I pass the pointer to
the object to the queue? Any other solution?
Dec 20 2006
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Orgoton wrote:
 I'm making a game and I have to keep track of the objects in the game, which
 inherit from a master object class called "objct". So I would make a class for
 "baddie", "player" and such. Now, to draw them I have to keep track of their
 existance, so, when one is created, it adds itself to the "frame queue" and
 removes itself when it is deleted. To keep track of them I created a "queue"
 class, which has a protected member called "objct *pters[]". It is an array of
 pointers so when I recieve a new actor for the queue, I increase the array
 size accordingly and on the next slot, just add it there. Simple. Now, the
 queue class has a method called "add" and "remove" both of them take as
 parameter "objct *target".
 
 To sum it up and finally tell you my question the objct has in it's
 constructor "framequeue.add(this);" and a correspondind "remove(this)".
 
 Apparently, "this" is not a pointer... as the compiler claims: "function
 queue.remoce (objct *target) does not match parameter types (objct)" and that
 "cannot implicitly convert expression (this) of type objct to objct*". So,
 does passing "this" create a copy of the object? How do I pass the pointer to
 the object to the queue? Any other solution?

Unless I am missing something, you shouldn't need to use pointers at all. Object variables in D are referances to the actual object which lives off in space somewhere. (Where space happens to be the heap.) I'm also curious as to how this Queue class of yours is designed. It sounds like there ought to be an easier means. -- Chris Nicholson-Sauls
Dec 20 2006
parent reply Orgoton <orgoton mindless.com> writes:
I haven't finished it yet. Well, each frame, the main engine will cycle through
all the pointers on the queue with something like

for (i=0; i<queue.size; i++) queue.next().draw();

Or, to save up the callings

queue.drawall()

where all pointers would be called.

I'm used to C++, sorry for the question about pointers and copies. That means I
can just make something like

objct objts[];

and use it just like a pointer...

void add (in objct target)
{ objts[objts.lenght-1]=target; }

(on objct)
this
{queue.add(this);}

and it'l work just fine without creating copies of the objects? (and spare me of
memory problems along the way :P)
Dec 20 2006
next sibling parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
== Quote from Orgoton (orgoton mindless.com)'s article
 void add (in objct target)
 { objts[objts.lenght-1]=target; }

use operator '~=' to 'push_back' objects to an dynamic array :) e.g.: void add (in objct target) { objts ~= target; }
Dec 20 2006
prev sibling next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Orgoton wrote:
 I haven't finished it yet. Well, each frame, the main engine will cycle through
 all the pointers on the queue with something like
 
 for (i=0; i<queue.size; i++) queue.next().draw();
 
 Or, to save up the callings
 
 queue.drawall()
 
 where all pointers would be called.
 
 I'm used to C++, sorry for the question about pointers and copies. That means I
 can just make something like
 
 objct objts[];
 
 and use it just like a pointer...
 
 void add (in objct target)
 { objts[objts.lenght-1]=target; }
 
 (on objct)
 this
 {queue.add(this);}
 
 and it'l work just fine without creating copies of the objects? (and spare me
of
 memory problems along the way :P)

That's right. You could also use array concatenation (objts ~= target;) if you aren't wanting to enforce a particular size. And since you plan to use this in a loop, it might be worth taking a look at foreach. -- Chris Nicholson-Sauls
Dec 20 2006
parent reply Orgoton <orgoton mindless.com> writes:
And when I want to remove the reference? Something like

void remove(in object target)
the_for:
for (i=0; i<queue.length; i++)
{
if target==queue[i]
{
queue[i]=queue[$-1];
queue.length-=1;
break the_for;
}
}

the compare will work fine, right? I mean, in C++ it would just compare a 4 byte
integer, how much data will D compare? hopefully, not he full object data ...
... ...
Dec 20 2006
next sibling parent reply BCS <BCS pathilink.com> writes:
I haven't used it but IIRC cashew has much of what you want.

http://www.dsource.org/projects/cashew
Dec 20 2006
parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
BCS wrote:
 I haven't used it but IIRC cashew has much of what you want.
 
 http://www.dsource.org/projects/cashew

It should. I was just trying to be nice and not go promoting it all over the place like I usually do. :) Also, as he said he is new to the language, I wasn't sure how he might feel about pseudo-members. Using Cashew, a Queue of type 'objct' as an array would look like: # import cashew .utils .array ; # # objct[] queue ; To add to the queue: # queue ~= target; To add multiple items: # queue.push(target1, target2, target3... targetN); To remove from the queue: # queue.remove(target); The only issue is if he wants the queue to be an exclusive set, in which case adding to the queue becomes: # queue ~= target; queue.unique(); Or he could write a 'qpush' like so: # void qpush (T) (inout T[] haystack, T[] bale ...) { # haystack.push(bale); # haystack.unique(); # } -- Chris Nicholson-Sauls
Dec 20 2006
parent reply BCS <BCS pathilink.com> writes:
Chris Nicholson-Sauls wrote:
 The only issue is if he wants the queue to be an exclusive set, in which 
 case adding to the queue becomes:
 # queue ~= target; queue.unique();
 
 Or he could write a 'qpush' like so:
 # void qpush (T) (inout T[] haystack, T[] bale ...) {
 #   haystack.push(bale);
 #   haystack.unique();
 # }
 
 -- Chris Nicholson-Sauls

It seems what he real wants is a set. How about use an AA: bool[Objct] set; set[something] = true; // add something foreach(k,_;set) k.draw(); // work all set.remove(something); // to get rid of things To bad void[T] isn't allowed <g>.
Dec 20 2006
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
BCS wrote:
 Chris Nicholson-Sauls wrote:
 The only issue is if he wants the queue to be an exclusive set, in 
 which case adding to the queue becomes:
 # queue ~= target; queue.unique();

 Or he could write a 'qpush' like so:
 # void qpush (T) (inout T[] haystack, T[] bale ...) {
 #   haystack.push(bale);
 #   haystack.unique();
 # }

 -- Chris Nicholson-Sauls

It seems what he real wants is a set. How about use an AA: bool[Objct] set; set[something] = true; // add something foreach(k,_;set) k.draw(); // work all set.remove(something); // to get rid of things To bad void[T] isn't allowed <g>.

That would be the ideal, if he is after a set. The void[T] did have its shining moments. :) Alas. -- Chris Nicholson-Sauls
Dec 20 2006
prev sibling next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Orgoton wrote:
 And when I want to remove the reference? Something like
 
 void remove(in object target)
 the_for:
 for (i=0; i<queue.length; i++)
 {
 if target==queue[i]
 {
 queue[i]=queue[$-1];
 queue.length-=1;
 break the_for;
 }
 }
 
 the compare will work fine, right? I mean, in C++ it would just compare a 4
byte
 integer, how much data will D compare? hopefully, not he full object data ...
... ...

Use 'is' instead of '=='. It will compare references.
Dec 20 2006
prev sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Orgoton wrote:
 And when I want to remove the reference? Something like
 
 void remove(in object target)
 the_for:
 for (i=0; i<queue.length; i++)
 {
 if target==queue[i]
 {
 queue[i]=queue[$-1];
 queue.length-=1;
 break the_for;
 }
 }
 
 the compare will work fine, right? I mean, in C++ it would just compare a 4
byte
 integer, how much data will D compare? hopefully, not he full object data ...
... ...

The compare will call opEquals, which will compare addresses by default. (Assuming the contents are class instances, not structs. Structs compare their contents by default) Unfortunately, decreasing the queue length will have to be done like this: queue.length = queue.length - 1; or: queue = queue[0 .. $-1]; because .length is a property, and as such op-assignments won't work, only direct assignment.
Dec 20 2006
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Frits van Bommel wrote:
 The compare will call opEquals, which will compare addresses by default. 
 (Assuming the contents are class instances, not structs. Structs compare 
 their contents by default)

And what Tom said: if you want to enforce identity-comparison (or avoid the virtual function-call) use 'is' instead of '=='.
Dec 20 2006
prev sibling parent reply BCS <BCS pathilink.com> writes:
Orgoton wrote:
 I haven't finished it yet. Well, each frame, the main engine will cycle through
 all the pointers on the queue with something like
 
 for (i=0; i<queue.size; i++) queue.next().draw();

Take a look at foreach. If queue is an array, this will work: foreach(item;queue) item.draw(); If it isn't an array, you can implement an opApply.
 
 That means I can just [...]
 
 objct objts[];
 
 void add (in objct target)
 { objts[objts.lenght-1]=target; }
 
 and it'l work just fine without creating copies of the objects? (and spare me
of
 memory problems along the way :P)

yup. BTW this is the same thing objts[$-1]=target;
Dec 20 2006
parent reply Tim Keating <holyspamster gmail.com> writes:
In article <emceee$2s0a$1 digitaldaemon.com>, BCS pathilink.com says...
 Take a look at foreach. If queue is an array, this will work:
 
 foreach(item;queue) item.draw();

Little syntax nitpick -- that needs to be foreach(auto item; queue) item.draw(), I believe. Normally I wouldn't fuss, but the OP did say he was new to the language :) TK
Dec 21 2006
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Tim Keating wrote:
 In article <emceee$2s0a$1 digitaldaemon.com>, BCS pathilink.com says...
 Take a look at foreach. If queue is an array, this will work:

 foreach(item;queue) item.draw();

Little syntax nitpick -- that needs to be foreach(auto item; queue) item.draw(), I believe. Normally I wouldn't fuss, but the OP did say he was new to the language :) TK

Actually, no. foreach( item; list ) { .. } works automagically :)
Dec 22 2006
prev sibling next sibling parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
== Quote from Orgoton (orgoton mindless.com)'s article
 I'm making a game and I have to keep track of the objects in the game, which
 inherit from a master object class called "objct". So I would make a class for
 "baddie", "player" and such. Now, to draw them I have to keep track of their
 existance, so, when one is created, it adds itself to the "frame queue" and
 removes itself when it is deleted. To keep track of them I created a "queue"
 class, which has a protected member called "objct *pters[]". It is an array of
 pointers so when I recieve a new actor for the queue, I increase the array
 size accordingly and on the next slot, just add it there. Simple. Now, the
 queue class has a method called "add" and "remove" both of them take as
 parameter "objct *target".
 To sum it up and finally tell you my question the objct has in it's
 constructor "framequeue.add(this);" and a correspondind "remove(this)".
 Apparently, "this" is not a pointer... as the compiler claims: "function
 queue.remoce (objct *target) does not match parameter types (objct)" and that
 "cannot implicitly convert expression (this) of type objct to objct*". So,
 does passing "this" create a copy of the object? How do I pass the pointer to
 the object to the queue? Any other solution?

RTM: use operator '&' to get its pointer. e.g. framequeue.add(&this); Anyway, I recommend you to use all objects by reference, no pointers at all. e.g.: object[] framequeue; framequeue ~= this; framequeue ~= another_object;
Dec 20 2006
prev sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
In addition to what the others have said, here are some more tips:

* All D class inherit from Object by default. So, if your "object" class 
doesn't do anything special, you can ditch it, and use the standard 
Object class.

* You can iterate over an array using foreach:
Object[] objs ..
foreach( Object object; objs )
{
     //do something
}

even better, foreach has magical type deduction, so you don't have to 
say "foreach Object object", just say:
foreach( object; object_list ) { .. }
The compiler will automatically deduce the type of "object" based on 
"object_list".

* You can remove an element from the array using a slicing trick:
void remove( Object[] array, int i )
{
      array = array[0..i] ~ array[i+1..$];
}
I haven't tested this but it should work, although I suspect it maybe 
suboptimal.
The $ inside the slice refers to array.length.


Orgoton wrote:
 I'm making a game and I have to keep track of the objects in the game, which
 inherit from a master object class called "objct". So I would make a class for
 "baddie", "player" and such. Now, to draw them I have to keep track of their
 existance, so, when one is created, it adds itself to the "frame queue" and
 removes itself when it is deleted. To keep track of them I created a "queue"
 class, which has a protected member called "objct *pters[]". It is an array of
 pointers so when I recieve a new actor for the queue, I increase the array
 size accordingly and on the next slot, just add it there. Simple. Now, the
 queue class has a method called "add" and "remove" both of them take as
 parameter "objct *target".
 
 To sum it up and finally tell you my question the objct has in it's
 constructor "framequeue.add(this);" and a correspondind "remove(this)".
 
 Apparently, "this" is not a pointer... as the compiler claims: "function
 queue.remoce (objct *target) does not match parameter types (objct)" and that
 "cannot implicitly convert expression (this) of type objct to objct*". So,
 does passing "this" create a copy of the object? How do I pass the pointer to
 the object to the queue? Any other solution?

Dec 21 2006
parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Hasan Aljudy wrote:
 * You can remove an element from the array using a slicing trick:
 void remove( Object[] array, int i )
 {
      array = array[0..i] ~ array[i+1..$];
 }
 I haven't tested this but it should work, although I suspect it maybe 
 suboptimal.
 The $ inside the slice refers to array.length.

It is "suboptimal" but not by /too/ much. Cashew uses C's memmove thanks to a couple of other D'ers who proved the performance differences. (I only mention it because he specifically said he's writing games, and performance would therefore be important.) In case he doesn't want all of Cashew, the implementation of .drop follows: void drop (T) (inout T[] haystack, size_t index) in { assert(index < haystack.length, ".drop() called with index greater than array length"); } body { if (index != haystack.length - 1) { memmove(&(haystack[index]), &(haystack[index + 1]), T.sizeof * (haystack.length - index)); } haystack.length = haystack.length - 1; } -- Chris Nicholson-Sauls
Dec 21 2006
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Chris Nicholson-Sauls wrote:
 void drop (T) (inout T[] haystack, size_t index)
 in {
   assert(index < haystack.length, ".drop() called with index greater 
 than array length");
 }
 body {
   if (index != haystack.length - 1) {
     memmove(&(haystack[index]), &(haystack[index + 1]), T.sizeof * 
 (haystack.length - index));
   }
   haystack.length = haystack.length - 1;
 }

Doesn't that move one element from beyond the end of the array? Array [0, 1, 2], drop element at index 1: memmove(&(array[1]), &(array[2]), int.sizeof * (3-1)); Moves 2 elements, starting at the 3rd element (element at index 2) of a 3-element array...
Dec 21 2006
parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Frits van Bommel wrote:
 Chris Nicholson-Sauls wrote:
 void drop (T) (inout T[] haystack, size_t index)
 in {
   assert(index < haystack.length, ".drop() called with index greater 
 than array length");
 }
 body {
   if (index != haystack.length - 1) {
     memmove(&(haystack[index]), &(haystack[index + 1]), T.sizeof * 
 (haystack.length - index));
   }
   haystack.length = haystack.length - 1;
 }

Doesn't that move one element from beyond the end of the array? Array [0, 1, 2], drop element at index 1: memmove(&(array[1]), &(array[2]), int.sizeof * (3-1)); Moves 2 elements, starting at the 3rd element (element at index 2) of a 3-element array...

Doesn't seem to. Illustrative program: # import cashew .utils .array ; # import std .stdio ; # # struct Foo { # int[] alpha = [0, 1, 2], # beta = [3, 4, 5]; # } # # void main () { # Foo foo ; # # writefln("foo.alpha == ", foo.alpha); # writefln("foo.beta == ", foo.beta ); # # foo.alpha.drop(1U); # # writefln("foo.alpha == ", foo.alpha); # writefln("foo.beta == ", foo.beta ); # } For me this output: foo.alpha == [0,1,2] foo.beta == [3,4,5] foo.alpha == [0,2] foo.beta == [3,4,5] Which seems to imply the data after alpha isn't being touched. That said, I'll try changing it anyhow. Just in case. -- Chris Nicholson-Sauls
Dec 21 2006
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Chris Nicholson-Sauls wrote:
 Frits van Bommel wrote:
 Chris Nicholson-Sauls wrote:
 void drop (T) (inout T[] haystack, size_t index)
 in {
   assert(index < haystack.length, ".drop() called with index greater 
 than array length");
 }
 body {
   if (index != haystack.length - 1) {
     memmove(&(haystack[index]), &(haystack[index + 1]), T.sizeof * 
 (haystack.length - index));
   }
   haystack.length = haystack.length - 1;
 }

Doesn't that move one element from beyond the end of the array? Array [0, 1, 2], drop element at index 1: memmove(&(array[1]), &(array[2]), int.sizeof * (3-1)); Moves 2 elements, starting at the 3rd element (element at index 2) of a 3-element array...

Doesn't seem to. Illustrative program: # import cashew .utils .array ; # import std .stdio ; # # struct Foo { # int[] alpha = [0, 1, 2], # beta = [3, 4, 5]; # } # # void main () { # Foo foo ; # # writefln("foo.alpha == ", foo.alpha); # writefln("foo.beta == ", foo.beta ); # # foo.alpha.drop(1U); # # writefln("foo.alpha == ", foo.alpha); # writefln("foo.beta == ", foo.beta ); # } For me this output: foo.alpha == [0,1,2] foo.beta == [3,4,5] foo.alpha == [0,2] foo.beta == [3,4,5] Which seems to imply the data after alpha isn't being touched. That said, I'll try changing it anyhow. Just in case.

How do you know it isn't touched? Just because it doesn't segfault doesn't mean it isn't accessing beyond array bounds... In fact, as long as the int after alpha is readable (i.e. the memory is mapped) you won't see a difference with that code. You'd probably see a difference if you kept a copy of alpha around and wrote that to output as well, though. (since the length of the copy doesn't get adjusted) In fact, I just tried that and the last element of alpha is overwritten by the first element of beta. This shouldn't happen if memmove() is called with the correct parameters. Another way to see this without keeping references to arrays being modified (which is arguably bad style) is the following: Replace the last argument of the memmove call with 'T.sizeof * (haystack.length - index - 1)' and notice your code above gives the exact same result...
Dec 21 2006
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Frits van Bommel wrote:
 Chris Nicholson-Sauls wrote:
 Frits van Bommel wrote:
 Chris Nicholson-Sauls wrote:
 void drop (T) (inout T[] haystack, size_t index)
 in {
   assert(index < haystack.length, ".drop() called with index greater 
 than array length");
 }
 body {
   if (index != haystack.length - 1) {
     memmove(&(haystack[index]), &(haystack[index + 1]), T.sizeof * 
 (haystack.length - index));
   }
   haystack.length = haystack.length - 1;
 }

Doesn't that move one element from beyond the end of the array? Array [0, 1, 2], drop element at index 1: memmove(&(array[1]), &(array[2]), int.sizeof * (3-1)); Moves 2 elements, starting at the 3rd element (element at index 2) of a 3-element array...

Doesn't seem to. Illustrative program: # import cashew .utils .array ; # import std .stdio ; # # struct Foo { # int[] alpha = [0, 1, 2], # beta = [3, 4, 5]; # } # # void main () { # Foo foo ; # # writefln("foo.alpha == ", foo.alpha); # writefln("foo.beta == ", foo.beta ); # # foo.alpha.drop(1U); # # writefln("foo.alpha == ", foo.alpha); # writefln("foo.beta == ", foo.beta ); # } For me this output: foo.alpha == [0,1,2] foo.beta == [3,4,5] foo.alpha == [0,2] foo.beta == [3,4,5] Which seems to imply the data after alpha isn't being touched. That said, I'll try changing it anyhow. Just in case.

How do you know it isn't touched? Just because it doesn't segfault doesn't mean it isn't accessing beyond array bounds... In fact, as long as the int after alpha is readable (i.e. the memory is mapped) you won't see a difference with that code. You'd probably see a difference if you kept a copy of alpha around and wrote that to output as well, though. (since the length of the copy doesn't get adjusted) In fact, I just tried that and the last element of alpha is overwritten by the first element of beta. This shouldn't happen if memmove() is called with the correct parameters.

Well, there we are then. :)
 Another way to see this without keeping references to arrays being 
 modified (which is arguably bad style) is the following:
 Replace the last argument of the memmove call with
 'T.sizeof * (haystack.length - index - 1)' and notice your code above 
 gives the exact same result...

Actually, this is exactly what I have done. Its in the repository now. -- Chris Nicholson-Sauls
Dec 22 2006