www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Static Constructors

reply "Saaa" <empty needmail.com> writes:
I've been using classes for a few months now and I keep on making arrays of 
instances like:

class Fruit
{
this( parameters )
{
}
..
}

Fruits fruit[];

void init()
{
fruit.length=fruit.length+1; // annoys me :)
fruit[$]=new Fruit(  .. );
}

deleting is even more annoying.

Now I've been reading about static constructors and is that somehow a 
solution to this manual array management?
Oct 03 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Saaa,

 I've been using classes for a few months now and I keep on making
 arrays of instances like:
 
 class Fruit
 {
 this( parameters )
 {
 }
 ..
 }
 Fruits fruit[];
 
 void init()
 {
 fruit.length=fruit.length+1; // annoys me :)
 fruit[$]=new Fruit(  .. );
 }
 deleting is even more annoying.
 
 Now I've been reading about static constructors and is that somehow a
 solution to this manual array management?
 
Sort of: switch "void init" for "static this" and you won't need to call init from main, but that's about all you will get. Also you can not control the order of execution. 2 minor nits:
 fruit[$]=new Fruit(  .. );
should be
 fruit[$-1]=new Fruit(  .. );
typo? If you are going to be adding more than very few items to the array, pick a better allocation scheme uint at = 0; if(extra <= at) data.length = (at+1)*2; data[at] = t; at++; //when done adding data.length = at; another option would be to use an AA if order isn't relevant. T[int] data; void reg(T b) { static int a = 0; data[a++] = b; }
Oct 03 2008
parent reply "Saaa" <empty needmail.com> writes:
 I've been using classes for a few months now and I keep on making
 arrays of instances like:

 class Fruit
 {
 this( parameters )
 {
 }
 ..
 }
 Fruits fruit[];

 void init()
 {
 fruit.length=fruit.length+1; // annoys me :)
 fruit[$]=new Fruit(  .. );
 }
 deleting is even more annoying.

 Now I've been reading about static constructors and is that somehow a
 solution to this manual array management?
Sort of: switch "void init" for "static this" and you won't need to call init from main, but that's about all you will get. Also you can not control the order of execution. 2 minor nits:
 fruit[$]=new Fruit(  .. );
should be
 fruit[$-1]=new Fruit(  .. );
typo?
yeah :), originally I did fruit[].length-1.
 If you are going to be adding more than very few items to the array, pick 
 a better allocation scheme

 uint at = 0;

 if(extra <= at) data.length = (at+1)*2;
extra=data.length; ?
 data[at] = t;
 at++;


 //when done adding
 data.length = at;
Initially I'd be adding a lot, but after that only sparsely.
 another option would be to use an AA if order isn't relevant.

 T[int] data;
Is this code? I mean should there be an int or 'int'?
 void reg(T b)
 {
  static int a = 0;
  data[a++] = b;
How does this work when it's no dynamic array?
 }
Oct 04 2008
parent reply BCS <ao pathlink.com> writes:
Reply to Saaa,

 uint at = 0;
 
 if(extra <= at) data.length = (at+1)*2;
 
extra=data.length; ?
Yes, Oops, (darn remnants :-)
 data[at] = t;
 at++;
 //when done adding
 data.length = at;
Initially I'd be adding a lot, but after that only sparsely.
 another option would be to use an AA if order isn't relevant.
 
 T[int] data;
 
Is this code? I mean should there be an int or 'int'?
It's a variables declaration (with T being the type that is needed).
 void reg(T b)
 {
 static int a = 0;
 data[a++] = b;
How does this work when it's no dynamic array?
http://www.digitalmars.com/d/1.0/arrays.html#associative It's a hash table, the key is "a". It's incremented so that you get a unique value for each insertion. ("data[rand()] = b;" might work also) you can also flip it the other way int[T] data; ... data[b] = 0; to access the data: data.keys.
 }
 
Oct 04 2008
parent "Saaa" <empty needmail.com> writes:
reading . . :) 
Oct 04 2008
prev sibling next sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Fri, Oct 3, 2008 at 5:57 PM, Saaa <empty needmail.com> wrote:
 I've been using classes for a few months now and I keep on making arrays of
 instances like:

 class Fruit
 {
 this( parameters )
 {
 }
 ..
 }

 Fruits fruit[];

 void init()
 {
 fruit.length=fruit.length+1; // annoys me :)
 fruit[$]=new Fruit(  .. );
 }
Have you heard of the append operator? fruit ~= new Fruit(...);
 deleting is even more annoying.
You don't have to delete anything. This is what the GC is for.
 Now I've been reading about static constructors and is that somehow a
 solution to this manual array management?
I don't know what you're trying to do, so it may or may not be a solution.
Oct 03 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Jarrett,

 On Fri, Oct 3, 2008 at 5:57 PM, Saaa <empty needmail.com> wrote:
 
 deleting is even more annoying.
 
You don't have to delete anything. This is what the GC is for.
delete could be "remove element 42 from an array of 54 resulting an an array of 53" data = data[0..42] ~ data[43..$]; or data[42..$-1] = data[43..$].dup; // in-place sortof data.length = data.length-1;
Oct 03 2008
parent reply "Saaa" <empty needmail.com> writes:
 deleting is even more annoying.
You don't have to delete anything. This is what the GC is for.
delete could be "remove element 42 from an array of 54 resulting an an array of 53" data = data[0..42] ~ data[43..$]; or data[42..$-1] = data[43..$].dup; // in-place sortof data.length = data.length-1;
Yes, like that :) What is the difference between those two? I mean, what do you mean by in-place.
Oct 04 2008
parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 04 Oct 2008 16:34:56 +0400, Saaa <empty needmail.com> wrote:

 deleting is even more annoying.
You don't have to delete anything. This is what the GC is for.
delete could be "remove element 42 from an array of 54 resulting an an array of 53" data = data[0..42] ~ data[43..$]; or data[42..$-1] = data[43..$].dup; // in-place sortof data.length = data.length-1;
Yes, like that :) What is the difference between those two? I mean, what do you mean by in-place.
First one allocates new memory block. Second one attempts to erase an element in-place. Dupping, however, allocates new memory, too. The best solution would be as follows: void eraseNth(ref T[] data, int n) { data[n] = data[$-1]; data.length = data.length - 1; }
Oct 04 2008
next sibling parent reply "Saaa" <empty needmail.com> writes:
 First one allocates new memory block.
 Second one attempts to erase an element in-place. Dupping, however, 
 allocates new memory, too.
 The best solution would be as follows:

 void eraseNth(ref T[] data, int n) {
     data[n] = data[$-1];
     data.length = data.length - 1;
 }
Yay, thats how I do it :)
Oct 04 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Saaa" <empty needmail.com> wrote in message 
news:gc8314$24e0$1 digitalmars.com...
 First one allocates new memory block.
 Second one attempts to erase an element in-place. Dupping, however, 
 allocates new memory, too.
 The best solution would be as follows:

 void eraseNth(ref T[] data, int n) {
     data[n] = data[$-1];
     data.length = data.length - 1;
 }
Yay, thats how I do it :)
If you want the memory removed as soon as possible, you should zero out the last element, otherwise, the GC will still think the element is being pointed to: data[n] = data[$-1]; data[$-1] = null; data.length = data.length - 1; -Steve
Oct 04 2008
next sibling parent "Saaa" <empty needmail.com> writes:
 If you want the memory removed as soon as possible, you should zero out 
 the
 last element, otherwise, the GC will still think the element is being 
 pointed to:

 data[n] = data[$-1];
 data[$-1] = null;
 data.length = data.length - 1;

 -Steve
Because the GC sees all allocated memory as used and an array doesn't deallocate memory when made smaller? Or just delete it :)
Oct 04 2008
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 If you want the memory removed as soon as possible, you should zero out the 
 last element, otherwise, the GC will still think the element is being 
 pointed to:
 data[n] = data[$-1];
 data[$-1] = null;
 data.length = data.length - 1;
Really?? Is that another bug of dynamic arrays? I have assumed that dynamic arrays do such things automatically. I do that in my collections, for example: ... void length(int newlen) { if (newlen < 0 || newlen > this._length) throw new ArgumentException("ArrayBuilder.length(newlen):" " newlen < 0 || newlen > ArrayBuilder.length"); static if (IsReferenceType!(T)) { static if (IsStaticArray!(T)) { T Tinit; this.data[newlen .. this._length][] = Tinit; } else { this.data[newlen .. this._length] = T.init; } } this._length = newlen; } ... If what you say is true, then it deserves a space on bugzilla and to be fixed soon. Bye, bearophile
Oct 04 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"bearophile" wrote
 Steven Schveighoffer:
 If you want the memory removed as soon as possible, you should zero out 
 the
 last element, otherwise, the GC will still think the element is being
 pointed to:
 data[n] = data[$-1];
 data[$-1] = null;
 data.length = data.length - 1;
Really?? Is that another bug of dynamic arrays? I have assumed that dynamic arrays do such things automatically. I do that in my collections, for example: ... void length(int newlen) { if (newlen < 0 || newlen > this._length) throw new ArgumentException("ArrayBuilder.length(newlen):" " newlen < 0 || newlen > ArrayBuilder.length"); static if (IsReferenceType!(T)) { static if (IsStaticArray!(T)) { T Tinit; this.data[newlen .. this._length][] = Tinit; } else { this.data[newlen .. this._length] = T.init; } } this._length = newlen; } ... If what you say is true, then it deserves a space on bugzilla and to be fixed soon.
Setting data.length to one less is equivalent to: data = data[0..$-1]; Which effectively leaves the pointer to the class untouched in the unused portion of the memory. However, the memory is still valid in terms of the GC. The GC scans the entire block for pointers, even if you aren't using all of the block. That is why it always zeros out memory before giving it to you. If setting the length to less than the original value did something special like zero out the now unused elements, then weird things would happen. for example: int[] data = [0,1,2,3,4].dup; int[] dataslice = data[0..2]; dataslice.length = 1; assert(data[1] == 1); // this would fail -Steve
Oct 04 2008
next sibling parent reply "Saaa" <empty needmail.com> writes:
 for example:

 int[] data = [0,1,2,3,4].dup;
why the dup?
 int[] dataslice = data[0..2];
 dataslice.length = 1;

 assert(data[1] == 1); // this would fail

 -Steve
Rest makes sense.
Oct 04 2008
parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 05 Oct 2008 01:37:30 +0400, Saaa <empty needmail.com> wrote:

 for example:

 int[] data = [0,1,2,3,4].dup;
why the dup?
because [0,1,2,3,4] is readonly
 int[] dataslice = data[0..2];
 dataslice.length = 1;

 assert(data[1] == 1); // this would fail

 -Steve
Rest makes sense.
Oct 04 2008
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 int[] data = [0,1,2,3,4].dup;
 int[] dataslice = data[0..2];
 dataslice.length = 1;
 assert(data[1] == 1); // this would fail
I see, the current light semantics of slices require the extra data to be kept. You are right, and I am glad to be wrong :-) (That collection I have shown can't be sliced, but in special situations that problem may be present. I'll have to think about it). Thank you for your answers, bye, bearophile
Oct 04 2008
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 04 Oct 2008 22:02:43 +0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 "Saaa" <empty needmail.com> wrote in message
 news:gc8314$24e0$1 digitalmars.com...
 First one allocates new memory block.
 Second one attempts to erase an element in-place. Dupping, however,
 allocates new memory, too.
 The best solution would be as follows:

 void eraseNth(ref T[] data, int n) {
     data[n] = data[$-1];
     data.length = data.length - 1;
 }
Yay, thats how I do it :)
If you want the memory removed as soon as possible, you should zero out the last element, otherwise, the GC will still think the element is being pointed to: data[n] = data[$-1]; data[$-1] = null; data.length = data.length - 1; -Steve
Great advice, thanks!
Oct 04 2008
prev sibling parent reply Gide Nwawudu <gide btinternet.com> writes:
On Sat, 4 Oct 2008 14:02:43 -0400, "Steven Schveighoffer"
<schveiguy yahoo.com> wrote:

"Saaa" <empty needmail.com> wrote in message 
news:gc8314$24e0$1 digitalmars.com...
 First one allocates new memory block.
 Second one attempts to erase an element in-place. Dupping, however, 
 allocates new memory, too.
 The best solution would be as follows:

 void eraseNth(ref T[] data, int n) {
     data[n] = data[$-1];
     data.length = data.length - 1;
 }
Yay, thats how I do it :)
If you want the memory removed as soon as possible, you should zero out the last element, otherwise, the GC will still think the element is being pointed to: data[n] = data[$-1]; data[$-1] = null; data.length = data.length - 1; -Steve
The following code outputs [0 4 2 3], is this a bug in D2? int n = 1; int[] data = [0,1,2,3,4].dup; writeln(data); // [0 1 2 3 4] data[n] = data[$-1]; data[$-1] = 0; data.length = data.length - 1; writeln(data); // [0 4 2 3] Gide
Oct 05 2008
next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 05 Oct 2008 21:26:58 +0400, Gide Nwawudu <gide btinternet.com>  
wrote:

 On Sat, 4 Oct 2008 14:02:43 -0400, "Steven Schveighoffer"
 <schveiguy yahoo.com> wrote:

 "Saaa" <empty needmail.com> wrote in message
 news:gc8314$24e0$1 digitalmars.com...
 First one allocates new memory block.
 Second one attempts to erase an element in-place. Dupping, however,
 allocates new memory, too.
 The best solution would be as follows:

 void eraseNth(ref T[] data, int n) {
     data[n] = data[$-1];
     data.length = data.length - 1;
 }
Yay, thats how I do it :)
If you want the memory removed as soon as possible, you should zero out the last element, otherwise, the GC will still think the element is being pointed to: data[n] = data[$-1]; data[$-1] = null; data.length = data.length - 1; -Steve
The following code outputs [0 4 2 3], is this a bug in D2? int n = 1; int[] data = [0,1,2,3,4].dup; writeln(data); // [0 1 2 3 4] data[n] = data[$-1]; data[$-1] = 0; data.length = data.length - 1; writeln(data); // [0 4 2 3] Gide
Errr... And what is an expected output? I believe the actual output is correct.
Oct 05 2008
prev sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sun, Oct 5, 2008 at 1:26 PM, Gide Nwawudu <gide btinternet.com> wrote:

 The following code outputs [0 4 2 3], is this a bug in D2?

 int n = 1;
 int[] data = [0,1,2,3,4].dup;
 writeln(data); // [0 1 2 3 4]
 data[n] = data[$-1];
now data is [0 4 2 3 4]
 data[$-1] = 0;
now data is [0 4 2 3 0]
 data.length = data.length - 1;
now data is [0 4 2 3]
 writeln(data); // [0 4 2 3]
And that's right. What's the issue?
Oct 05 2008
parent reply Gide Nwawudu <gide btinternet.com> writes:
On Sun, 5 Oct 2008 15:31:38 -0400, "Jarrett Billingsley"
<jarrett.billingsley gmail.com> wrote:

On Sun, Oct 5, 2008 at 1:26 PM, Gide Nwawudu <gide btinternet.com> wrote:

 The following code outputs [0 4 2 3], is this a bug in D2?

 int n = 1;
 int[] data = [0,1,2,3,4].dup;
 writeln(data); // [0 1 2 3 4]
 data[n] = data[$-1];
now data is [0 4 2 3 4]
 data[$-1] = 0;
now data is [0 4 2 3 0]
 data.length = data.length - 1;
now data is [0 4 2 3]
 writeln(data); // [0 4 2 3]
And that's right. What's the issue?
My main issue was with eraseNth re-ordering elements, but if moving the end element over the nth is standard, then I suppose it's ok. I would have expected a 'stable' version, i.e. [0 2 3 4] as the result. Gide
Oct 05 2008
parent "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sun, Oct 5, 2008 at 5:32 PM, Gide Nwawudu <gide btinternet.com> wrote:
 On Sun, 5 Oct 2008 15:31:38 -0400, "Jarrett Billingsley"
 <jarrett.billingsley gmail.com> wrote:

On Sun, Oct 5, 2008 at 1:26 PM, Gide Nwawudu <gide btinternet.com> wrote:

 The following code outputs [0 4 2 3], is this a bug in D2?

 int n = 1;
 int[] data = [0,1,2,3,4].dup;
 writeln(data); // [0 1 2 3 4]
 data[n] = data[$-1];
now data is [0 4 2 3 4]
 data[$-1] = 0;
now data is [0 4 2 3 0]
 data.length = data.length - 1;
now data is [0 4 2 3]
 writeln(data); // [0 4 2 3]
And that's right. What's the issue?
My main issue was with eraseNth re-ordering elements, but if moving the end element over the nth is standard, then I suppose it's ok. I would have expected a 'stable' version, i.e. [0 2 3 4] as the result. Gide
It's the faster method is all. If you want a stable remove, you can either copy everything after the removed element down a slot, or you can create a new array with "a[0 .. n] ~ a[n + 1 .. $]". The former requires less memory and less strain on the GC, and unless you have a really fancy compiler that will autoparallelize the copy from the old array into the new, the second solution will be no faster at copying the data.
Oct 05 2008
prev sibling parent BCS <ao pathlink.com> writes:
Reply to Denis,

 On Sat, 04 Oct 2008 16:34:56 +0400, Saaa <empty needmail.com> wrote:
 
 deleting is even more annoying.
 
You don't have to delete anything. This is what the GC is for.
delete could be "remove element 42 from an array of 54 resulting an an array of 53" data = data[0..42] ~ data[43..$]; or data[42..$-1] = data[43..$].dup; // in-place sortof data.length = data.length-1;
Yes, like that :) What is the difference between those two? I mean, what do you mean by in-place.
First one allocates new memory block. Second one attempts to erase an element in-place. Dupping, however, allocates new memory, too. The best solution would be as follows: void eraseNth(ref T[] data, int n) { data[n] = data[$-1]; data.length = data.length - 1; }
The added speed from array copy optimizations might overcome the cost of alocing. OTOH use memmove and you will do even better.
Oct 04 2008
prev sibling parent reply "Saaa" <empty needmail.com> writes:
 Have you heard of the append operator?

 fruit ~= new Fruit(...);
Yeah, but never used it other than with strings . . silly me.
 deleting is even more annoying.
You don't have to delete anything. This is what the GC is for.
I think I don't get GC use. How do I actually delete one of those fruits then?
Oct 04 2008
parent reply bearophile <bearophileHUGS lycos.com> writes:
Saaa:

 I think I don't get GC use. How do I actually delete one of those fruits 
 then?
The GC deletes an object (usually, but sometimes it doesn't do it because it's not a precise GC) when there are no references to/from it. So if you keep it just inside an array, and it has no inbound references, and you remove it somehow (for example overwriting it with the last item of the array and then reducing the length of the array by 1), the GC loses the only reference to that objects, and deallocates it. Note the same thing happens in most languages/systems that have a GC. If your object manages some other resources beside its memory, for example an open file, you have to close it inside the destructor of your object. If the not precise GC keeps a spurious (wrong) reference to your object (because somewhere an int value looks like a pointer to the object memory block), then you are in troubles... Bye, bearophile
Oct 04 2008
parent reply "Saaa" <empty needmail.com> writes:
Thanks, from this I'd rather delete them manually :)

 The GC deletes an object (usually, but sometimes it doesn't do it because 
 it's not a precise GC) when there are no references to/from it.

 So if you keep it just inside an array, and it has no inbound references, 
 and you remove it somehow (for example overwriting it with the last item 
 of the array and then reducing the length of the array by 1), the GC loses 
 the only reference to that objects, and deallocates it.

 Note the same thing happens in most languages/systems that have a GC.

 If your object manages some other resources beside its memory, for example 
 an open file, you have to close it inside the destructor of your object. 
 If the not precise GC keeps a spurious (wrong) reference to your object 
 (because somewhere an int value looks like a pointer to the object memory 
 block), then you are in troubles...

 Bye,
 bearophile 
Oct 04 2008
parent bearophile <bearophileHUGS lycos.com> writes:
Saaa Wrote:

 Thanks, from this I'd rather delete them manually :)
No, deleting them manually is un-D-tonic :-) In most situations you let the GC do its work. Bye, bearophile
Oct 04 2008
prev sibling next sibling parent reply "Saaa" <empty needmail.com> writes:
Can I do something like this, or should I use the static constructor for 
this?
If so, how should I set the first parameter:size ?

Fruits fruit[];


class Fruit
{
this( parameters )
{
fruit[].length=fruit[].length+1;
fruit[$-1]=this;
}
 ..
void stuff()
{
}
..
}

new fruit( .. );
new fruit( .. );

foreach (Fruit f; fruit)
{
f.stuff();
}
Oct 04 2008
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Saaa" wrote
 Can I do something like this, or should I use the static constructor for 
 this?
 If so, how should I set the first parameter:size ?

 Fruits fruit[];
typo, and you are using C-style array syntax (which works, but is discouraged). This should be: Fruit[] fruit; Means, 'I declare an array of Fruit called fruit'
 class Fruit
 {
 this( parameters )
 {
 fruit[].length=fruit[].length+1;
 fruit[$-1]=this;
Don't do the [] operator, I think this may create a temporary array struct, and would not affect the global variable at all: fruit.length = fruit.length + 1; But instead of this, it's probably better to write: fruit ~= this; Which does all that work for you :)
 }
 ..
 void stuff()
 {
 }
 ..
 }

 new fruit( .. );
 new fruit( .. );

 foreach (Fruit f; fruit)
 {
 f.stuff();
 }
This should work, but be aware that it's not thread safe if you are using multiple threads. -Steve
Oct 04 2008
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Saaa:
 foreach (Fruit f; fruit)
 {
 f.stuff();
 }
Note that often this is enough: foreach (f; fruit) f.stuff; But I generally put the parentheses: foreach (f; fruit) f.stuff(); Bye, bearophile
Oct 04 2008
parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sat, Oct 4, 2008 at 2:22 PM, bearophile <bearophileHUGS lycos.com> wrote:
 Saaa:
 foreach (Fruit f; fruit)
 {
 f.stuff();
 }
Note that often this is enough: foreach (f; fruit) f.stuff; But I generally put the parentheses: foreach (f; fruit) f.stuff(); Bye, bearophile
Uh, ok, who cares what coding style you use?
Oct 04 2008
parent bearophile <bearophileHUGS lycos.com> writes:
Jarrett Billingsley:
 Uh, ok, who cares what coding style you use?
Python and Ruby teach that in a language a more uniform coding style helps people understand each other code faster (and similar things), and that leads to more modules done. And nowadays already done modules that you can find on the net make a language successful instead of an ignored one. I know that the usual culture of C/C++ may be a little against such "freedoms", so I presume a significant part of the D community may not appreciate that. That also shows a disadvantage of Lisp-style macros, because having lot of customizations makes code less easy to understand by other people. And the OP may be unaware of some of the syntactic peculiarities of D, so it's useful to show them. Bye, bearophile
Oct 04 2008
prev sibling parent "Saaa" <empty needmail.com> writes:
What would happen here?
Would the GC delete the newly made instances as there is no reference to it?

Fruits fruit[];
class Fruit
{
this( parameters )
{

}
..
void stuff()
{
}
 ..
}

new fruit( .. );
//or can this only be called if there is an static constructor?
new fruit( .. );

foreach (f; fruit)
  f.stuff();
//fruit is empty.
Oct 04 2008
prev sibling parent "Saaa" <empty needmail.com> writes:
Thanks!
I love these newsgroups :) 
Oct 04 2008