11

Just curious if this is the best practice for initializing a dynamic, multidimensional array in D. There is a section on arrays in their language reference, but I'm not quite sure if it goes over what I'm trying to accomplish.

class Map {
    Tile[][] tiles;

    this(uint width, uint height) {
        tiles.length = height;
        foreach (ref tilerow; tiles)
            tilerow.length = width;
    }
}

Map map1 = new Map(5000, 3000); // values determined at runtime

(or an equivalent alternative like a typical for (y=0;y<height;y++) loop).

My concern with this is that it reallocates each row of the array separately rather than the whole chunk all at once, so I don't know if this will lead to too much memory shuffling. Also, I believe it's not guaranteed to be contiguous (since tiles is just an array of pointers in this case). Is there any "better" way to do this (that doesn't involve using a single-dimensional array and computing the index myself)? As far as I can tell from the docs a contiguous multidimensional array can only be declared with immutable dimensions at compile time, just wondering if I'm missing something...

ccjuju
  • 507
  • 4
  • 15

2 Answers2

18

You can new the array, at least in D2:

Tile[][] tiles = new Tile[][](height, width);

I believe this is the best practice.

Justin W
  • 2,077
  • 12
  • 17
  • 2
    Well technically, _best_ practice would use `auto` on the left-hand side rather than repeating the type. But yeah, this is the best way to allocate a multi-dimensional array as long as each of them is supposed to be the same length. – Jonathan M Davis Dec 14 '11 at 21:07
  • Also see [http://www.d-programming-language.org/expression.html#NewExpression](http://www.d-programming-language.org/expression.html#NewExpression). – XP1 Feb 18 '12 at 21:36
3

you can fudge it by mallocing every thing you need upfront

this(uint width, uint height) {
    void* p = enforce(GC.malloc(Tile.sizeof*width*height),new OutOfMemoryException);
          //allocate all rows at once, throw on returned null
    tiles.length = height;
    foreach (i,ref tilerow; tiles)
        tilerow = cast(Tile[])p[Tile.sizeof*width*i..Tile.sizeof*width*(i+1)];
                //slice it into the multidimensional array
}

EDIT or use a temporary array to keep hem in for cleaner/less bugprone code (i.e. hide the malloc)

this(uint width, uint height) {
    Tile[] p = new Tile[height*width]
    tiles.length = height;
    foreach (i,ref tilerow; tiles)
        tilerow = p[width*i..width*(i+1)];
                //slice it into the multidimensional array
}
ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • 1
    Small note for the first sample: You can use enforceEx, e.g. `enforceEx!OutOfMemoryError(GC.malloc(Tile.sizeof*width*height));`, also it's not `OutOfMemoryException`, but `OutOfMemoryError`, and these require imports to `std.exception`, `core.memory` and `core.exception`. – Andrej Mitrović Dec 14 '11 at 15:30