December 20, 2006
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.
December 20, 2006
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 '=='.
December 20, 2006
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
December 20, 2006
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>.
December 20, 2006
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
December 21, 2006
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?
December 21, 2006
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
December 21, 2006
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...
December 21, 2006
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
December 22, 2006
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...