www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to pass in reference a fixed array in parameter

reply Eric P626 <ericp lariennalibrary.com> writes:
I am currently trying to learn how to program in D. I thought 
that I could start by trying some maze generation algorithms. I 
have a maze stored as 2D array of structure defined as follow 
which keep tracks of wall positions:

~~~
struct s_cell
{
    bool north = true;
    bool east = true;
    bool south = true;
    bool west = true;
}
~~~

I try to create a 2D array of fixed length and pass it in 
parameter as a reference. Normally, in C, I would have used a 
pointer as parameter, and pass the address of the array. Here, I 
thought it would have been easier just to pass a slice of the 
array, since a slice is a reference to the original array. So I 
wrote the signature like this:

~~~
void main()
{  writeln("Maze generation demo");

    s_cell [5][5] maze;
    print_maze (maze);

}

void print_maze ( s_cell [][] maze )
{
}
~~~

My idea is that print_maze use a slice of what ever is sent in 
parameter. Unfortunately, I get the following error message:

~~~
Error: function `mprmaze.print_maze(s_cell[][] maze)` is not 
callable using argument types `(s_cell[5][5])`
   cannot pass argument `maze` of type `s_cell[5][5]` to parameter 
`s_cell[][] maze`
~~~

I tried to find a solution on the internet, but could not find 
anything, I stumble a lot on threads about Go or Rust language 
even if I specify "d language" in my search.

Else is there other ways to pass an array as reference using 
parameter modifiers like: ref,in,out ...

Else, can it be done the C way using pointers?

Thank you.
Jun 04
next sibling parent evilrat <evilrat666 gmail.com> writes:
On Tuesday, 4 June 2024 at 12:22:23 UTC, Eric P626 wrote:
 I am currently trying to learn how to program in D. I thought 
 that I could start by trying some maze generation algorithms. I 
 have a maze stored as 2D array of structure defined as follow 
 which keep tracks of wall positions:

 ~~~
 struct s_cell
 {
    bool north = true;
    bool east = true;
    bool south = true;
    bool west = true;
 }
 ~~~

 I try to create a 2D array of fixed length and pass it in 
 parameter as a reference. Normally, in C, I would have used a 
 pointer as parameter, and pass the address of the array. Here, 
 I thought it would have been easier just to pass a slice of the 
 array, since a slice is a reference to the original array. So I 
 wrote the signature like this:

 ~~~
 void main()
 {  writeln("Maze generation demo");

    s_cell [5][5] maze;
    print_maze (maze);

 }

 void print_maze ( s_cell [][] maze )
 {
 }
 ~~~

 My idea is that print_maze use a slice of what ever is sent in 
 parameter. Unfortunately, I get the following error message:

 ~~~
 Error: function `mprmaze.print_maze(s_cell[][] maze)` is not 
 callable using argument types `(s_cell[5][5])`
   cannot pass argument `maze` of type `s_cell[5][5]` to 
 parameter `s_cell[][] maze`
 ~~~

 I tried to find a solution on the internet, but could not find 
 anything, I stumble a lot on threads about Go or Rust language 
 even if I specify "d language" in my search.
You have declared static array here, they cannot be implicitly converted to dynamic arrays. It is not very obvious but it is a part of language design to avoid unnecessary GC allocations and for C compatibility reasons in some cases (e.g. strings known at compile implicitly has null appended to it to be able to pass pointer as is to C functions). IIRC you can explicitly cast it to s_cell[][] to make it work but it will allocate new array when you append to it.
 Else is there other ways to pass an array as reference using 
 parameter modifiers like: ref,in,out ...
`ref` is exactly for that.
 Else, can it be done the C way using pointers?
absolutely, even ref behind the scenes will basically do the same thing anyway.
Jun 04
prev sibling next sibling parent reply Andy Valencia <dont spam.me> writes:
On Tuesday, 4 June 2024 at 12:22:23 UTC, Eric P626 wrote:

 I tried to find a solution on the internet, but could not find 
 anything, I stumble a lot on threads about Go or Rust language 
 even if I specify "d language" in my search.
Aside from the excellent answer already present, I wanted to mention that searching with "dlang" has helped target my searches. Welcome to D! (From another newbie.) Andy
Jun 04
parent reply Eric P626 <ericp lariennalibrary.com> writes:
On Tuesday, 4 June 2024 at 16:19:39 UTC, Andy Valencia wrote:
 On Tuesday, 4 June 2024 at 12:22:23 UTC, Eric P626 wrote:

 I tried to find a solution on the internet, but could not find 
 anything, I stumble a lot on threads about Go or Rust language 
 even if I specify "d language" in my search.
Aside from the excellent answer already present, I wanted to mention that searching with "dlang" has helped target my searches. Welcome to D! (From another newbie.) Andy
Thanks for the comments. So far, I only managed to make it work by creating a dynamic array and keeping the same signature: ~~~ void main() { s_cell [][] maze = new s_cell[][](5,5); print_maze (maze); } void print_maze ( s_cell [][] maze ) { } ~~~ Now according to the book, it's possible to assign a slice from a fixed array. This code will compile: ~~~ int[12] monthDays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; int[] a_slice = monthDays; ~~~ How come the assignment does not work when passing a parameter. I tried the following and it failed: ~~~ s_cell [5][5] maze; s_cell [][] sliced_maze = maze; ~~~ with this message: ~~~ Error: cannot implicitly convert expression `maze` of type `s_cell[5][5]` to `s_cell[][]` ~~~ Is it because it's a 2D array (slice of slice)? I need to manually copy each slice manually, or use a utility function to do the copy? This is why it cannot auto-magically do it with just when passing a parameter. I tried the following signatures with the ref keyword and it did not change anything: ~~~ void print_maze ( ref s_cell maze ) void print_maze ( ref s_cell [][] maze ) ~~~ From what I found, arrays passed in parameters are always passed by reference. So the ref keyword seems pointless. ------------------------------------------------------- The only solution left is to use pointers. But even this does not seems to work as in C. I created a function with different pointer signature and they all fails. Normally in C, this would have worked: ~~~ s_cell [5][5] maze; create_maze(&maze); void create_maze ( s_cell *maze) { } ~~~ I get the following error ~~~ Error: function `mprmaze.create_maze(s_cell* maze)` is not callable using argument types `(s_cell[5][5]*)` cannot pass argument `& maze` of type `s_cell[5][5]*` to parameter `s_cell* maze` ~~~ But I get the idea of ambiguity, is the pointer pointing on a single cell, or an array of cells, so there might need a way to specify that it's not just an elements. I tried this: ~~~ s_cell [5][5] maze; create_maze(&maze); void create_maze ( s_cell [][]*maze) { } ~~~ and get this error ~~~ Error: function `mprmaze.create_maze(s_cell[][]* maze)` is not callable using argument types `(s_cell[5][5]*)` cannot pass argument `& maze` of type `s_cell[5][5]*` to parameter `s_cell[][]* maze` ~~~ Now I think it expect a 2D array of pointers instead of a pointer on a 2D array. It's also not clear if there is a difference between those 2 notations: ~~~ &maze maze.ptr ~~~ Do you have a code sample on how to pass a 2D array by pointer? So far, the pointer solution seems like the only method that should be compatible with both fixed and dynamic arrays unless I am mistaken.
Jun 04
next sibling parent reply evilrat <evilrat666 gmail.com> writes:
On Wednesday, 5 June 2024 at 06:22:34 UTC, Eric P626 wrote:
 On Tuesday, 4 June 2024 at 16:19:39 UTC, Andy Valencia wrote:
 On Tuesday, 4 June 2024 at 12:22:23 UTC, Eric P626 wrote:
Thanks for the comments. So far, I only managed to make it work by creating a dynamic array and keeping the same signature: ~~~ void main() { s_cell [][] maze = new s_cell[][](5,5); print_maze (maze); } void print_maze ( s_cell [][] maze ) { } ~~~ Now according to the book, it's possible to assign a slice from a fixed array. This code will compile: ~~~ int[12] monthDays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; int[] a_slice = monthDays; ~~~
for simple cases like this it might work, but 2d array is not even contiguous, simpler case like s_cell[5][] might work too.
 How come the assignment does not work when passing a parameter. 
 I tried the following and it failed:

 ~~~
 s_cell [5][5] maze;
 s_cell [][] sliced_maze = maze;
 ~~~

 with this message:

 ~~~
 Error: cannot implicitly convert expression `maze` of type 
 `s_cell[5][5]` to `s_cell[][]`
 ~~~

 Is it because it's a 2D array (slice of slice)? I need to 
 manually copy each slice manually, or use a utility function to 
 do the copy? This is why it cannot auto-magically do it with 
 just when passing a parameter.
very likely this is the only solution - make a dynamic array by copying all elements. there was few old bug tracker issues discussed wrt to static arrays and join function but there is seems to be no agreement so far.
 ~~~
 Error: function `mprmaze.create_maze(s_cell[][]* maze)` is not 
 callable using argument types `(s_cell[5][5]*)`
 cannot pass argument `& maze` of type `s_cell[5][5]*` to 
 parameter `s_cell[][]* maze`
 ~~~

 Now I think it expect a 2D array of pointers instead of a 
 pointer on a 2D array.

 It's also not clear if there is a difference between those 2 
 notations:

 ~~~
 &maze
 maze.ptr
 ~~~
there is, array itself is a tuple of length and pointer, the .ptr notation is just the data location, this is what you usually pass to C functions and not &maze itself. to sum up, i wasn't able to make fixed-size arrays to work with dynamic arrays without making a copy, and I don't think this will change in the future because of various reasons including type system limitations and binary object formats. so if you really absolutely need static arrays for example to avoid GC allocations in hot path than you need to make function that takes fixed size array. in addition to that spec (https://dlang.org/spec/arrays.html#static-arrays) says static arrays is passed by value, unlike dynamic arrays that even when passed as length-and-pointer tuple will allow writing back to original data.
Jun 05
parent reply Nick Treleaven <nick geany.org> writes:
On Wednesday, 5 June 2024 at 09:24:23 UTC, evilrat wrote:
 for simple cases like this it might work, but 2d array is not 
 even contiguous,
A 2D static array is contiguous: https://dlang.org/spec/arrays.html#rectangular-arrays
 D static arrays, while using the same syntax, are implemented 
 as a fixed rectangular layout in a contiguous block of memory
Jun 05
parent evilrat <evilrat666 gmail.com> writes:
On Wednesday, 5 June 2024 at 11:27:32 UTC, Nick Treleaven wrote:
 On Wednesday, 5 June 2024 at 09:24:23 UTC, evilrat wrote:
 for simple cases like this it might work, but 2d array is not 
 even contiguous,
A 2D static array is contiguous: https://dlang.org/spec/arrays.html#rectangular-arrays
 D static arrays, while using the same syntax, are implemented 
 as a fixed rectangular layout in a contiguous block of memory
Yeah ok, i might have messed up with columns last time, but it works. ```d int[5][5] test; foreach(i; 0..5) { foreach(j; 0..5) { test[i][j] = i * 5 + j; } } foreach(i; 0..25) { assert(test[0].ptr[i] == i); } ```
Jun 05
prev sibling next sibling parent Nick Treleaven <nick geany.org> writes:
On Wednesday, 5 June 2024 at 06:22:34 UTC, Eric P626 wrote:
 Now according to the book, it's possible to assign a slice from 
 a fixed array. This code will compile:

 ~~~
 int[12] monthDays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 
 30, 31 ];
 int[] a_slice = monthDays;
 ~~~
The element types are both int, so the compiler can slice the static array. As if you had written `a_slice = monthDays[];`.
 How come the assignment does not work when passing a parameter. 
 I tried the following and it failed:

 ~~~
 s_cell [5][5] maze;
The element type is s_cell[5].
 s_cell [][] sliced_maze = maze;
The element type of sliced_maze is s_cell[], so the element types are incompatible.
 ~~~
 void print_maze ( ref s_cell maze )
 void print_maze ( ref s_cell [][] maze )
 ~~~

 From what I found, arrays passed in parameters are always 
 passed by reference. So the ref keyword seems pointless.
You don't need `ref` to be able to read the array length and elements. However, if you want to modify the array length, and have it affect the caller's dynamic array, you need `ref`.
 -------------------------------------------------------

 The only solution left is to use pointers. But even this does 
 not seems to work as in C. I created a function with different 
 pointer signature and they all fails.

 Normally in C, this would have worked:

 ~~~
 s_cell [5][5] maze;
 create_maze(&maze);
Pass `&maze[0][0]` instead.
 ~~~
 Error: function `mprmaze.create_maze(s_cell[][]* maze)` is not 
 callable using argument types `(s_cell[5][5]*)`
 cannot pass argument `& maze` of type `s_cell[5][5]*` to 
 parameter `s_cell[][]* maze`
 ~~~
s_cell[5][5] cannot implicitly convert to s_cell[][].
 Now I think it expect a 2D array of pointers instead of a 
 pointer on a 2D array.

 It's also not clear if there is a difference between those 2 
 notations:

 ~~~
 &maze
 maze.ptr
 ~~~
&maze is a pointer to s_cell[5][5]. maze.ptr is a pointer to s_cell[5]. `.ptr` means a pointer to the first element of the array.
Jun 05
prev sibling parent bauss <jacobbauss gmail.com> writes:
On Wednesday, 5 June 2024 at 06:22:34 UTC, Eric P626 wrote:
 I tried the following signatures with the ref keyword and it 
 did not change anything:

 ~~~
 void print_maze ( ref s_cell maze )
 void print_maze ( ref s_cell [][] maze )
 ~~~

 From what I found, arrays passed in parameters are always 
 passed by reference. So the ref keyword seems pointless.
There is one useful functionality about the ref keyword when passing arrays, it is that you can change the original array reference to another array reference. Ex. ``` void foo(ref int[] x) { x = [1,2,3]; } void bar(int[] y) { y = [1,2,3]; } void main() { auto x = [0,0,0]; auto y = [1,1,1]; foo(x); bar(y); writeln(x); writeln(y); } ``` The output of the program is: ``` [1, 2, 3] [1, 1, 1] ``` Of course in your case this doesn't matter, but just wanted to point out that adding ref to array parameters actually pose a function.
Jun 05
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
On Tuesday, 4 June 2024 at 12:22:23 UTC, Eric P626 wrote:
 I try to create a 2D array of fixed length and pass it in 
 parameter as a reference. Normally, in C, I would have used a 
 pointer as parameter, and pass the address of the array.
Not obvious what you're trying to do. How would you do it in C? Use one dimensional array? You can use one dimensional array in D too. If dimensions of the maze are dynamic, you just write the maze creation function that allocates the maze as you want. In simple case: ``` void main() { writeln("Maze generation demo"); s_cell [5][5] maze; print_maze (maze); } void print_maze (ref s_cell [5][5] maze ) { } ``` With factory: ``` void main() { s_cell[][] maze=make(5,5); print_maze(maze); } void print_maze(s_cell[][] maze) { } s_cell[][] make(int width, int height) { } ```
Jun 05
parent Kagamin <spam here.lot> writes:
With accessor:
```
void main()
{
     s_cell[] maze=make(5,5);
     s_cell a=maze.get(1,2);
     print_maze(maze);
}

void print_maze(s_cell[] maze)
{
}

s_cell[] make(int width, int height)
{
     return new s_cell[width*height];
}

s_cell get(s_cell[] maze, int x, int y)
{
     return maze[5*y+x]; //oops
}
```
looks like you need to store the maze width somewhere.
Jun 05
prev sibling next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Tuesday, 4 June 2024 at 12:22:23 UTC, Eric P626 wrote:
 ~~~
 void main()
 {  writeln("Maze generation demo");

    s_cell [5][5] maze;
    print_maze (maze);

 }

 void print_maze ( s_cell [][] maze )
 {
 }
 ~~~
This is how to do it without GC allocations (I have used `int` instead for demo purposes): ```d import std.stdio; alias s_cell = int; void main() { writeln("Maze generation demo"); s_cell [5][5] maze = [0, 1, 2, 3, 4]; s_cell[][5] slices; // static array of 5 slices foreach (i, row; maze) slices[i] = row; print_maze (slices); } //~ void print_maze ( s_cell [][] maze... ) void print_maze ( s_cell [][] maze ) { foreach (a; maze) a.writeln(); } ```
Jun 05
next sibling parent Nick Treleaven <nick geany.org> writes:
On Wednesday, 5 June 2024 at 10:27:47 UTC, Nick Treleaven wrote:
 //~ void print_maze ( s_cell [][] maze... )
I meant to delete that line!
Jun 05
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Wednesday, 5 June 2024 at 10:27:47 UTC, Nick Treleaven wrote:
    foreach (i, row; maze)
        slices[i] = row;
Sorry that assignment was wrong (edited at last minute). Fixed: ```d import std.stdio; alias s_cell = int; void main() { writeln("Maze generation demo"); s_cell [5][5] maze; int n; foreach (i, row; maze) foreach (j, col; row) maze[i][j] = n++; s_cell[][5] slices; foreach (i, _; maze) slices[i] = maze[i]; print_maze (slices); } void print_maze ( s_cell [][] maze ) { foreach (a; maze) a.writeln(); } ```
Jun 05
parent Eric P626 <ericp lariennalibrary.com> writes:
On Wednesday, 5 June 2024 at 10:36:50 UTC, Nick Treleaven wrote:
 ```d
 import std.stdio;
 alias s_cell = int;

 void main()
 {  writeln("Maze generation demo");

    s_cell [5][5] maze;
    int n;
    foreach (i, row; maze)
         foreach (j, col; row)
             maze[i][j] = n++;

    s_cell[][5] slices;
    foreach (i, _; maze)
        slices[i] = maze[i];

    print_maze (slices);
 }

 void print_maze ( s_cell [][] maze )
 {
     foreach (a; maze)
         a.writeln();
 }
 ```
Thanks for the feedback Almost all my projects works intensively with multiple dimensions arrays. So I want to know the best way to manage multi dimensional arrays. I guess the best solution so far are: 1) Only use dynamic arrays. 2) Use a single dimension array, and compute the index value from x,y,z coordinates (Makes dynamic allocation easier). This solution could work well with pointers too. 3) Make my own data structure or class containing the array. Allowing to pass the structure/class by reference. Could allow encapsulating single or multi dimensional arrays. About .ptr, the documentation page state that: ~~~ The .ptr property for static and dynamic arrays will give the address of the first element in the array: ~~~ So I assumed that the following expressions where equivalent, but I guess the multiple dimensions do complicate things: ~~~ array.ptr == &array == &array[0] ~~~ So ommitting the "ref" keyword it's like if the data was read only even if the variable is not passed by value. That means that this signature cannot modify the content of the 2D array: ~~~ void print_maze ( s_cell [][] maze ) ~~~ For the create_maze() function, I would need to use the follwing signature since it changes the content of the array. ~~~ void print_maze ( ref s_cell [][] maze ) ~~~ I imagine `a.writeln();` is the same as `writeln(a);` ? Your foreach loops look better than mine. Here is the code I have been using to print the maze. ~~~ void print_maze ( s_cell [][] maze ) { //print top row, assume full foreach ( cell; maze[0] ) write("+---"); writeln("+"); for ( int y = 0; y < maze.length; y++) { //print content write("|"); //assume edge is always full for ( int x = 0; x < maze[y].length; x++) { write(" "); write( maze[y][x].east ? "|": " "); } writeln(); foreach ( cell; maze[y] ) write( cell.south ? "+---" : " " ); writeln("+"); } } ~~~ your iteration version is more neat: ~~~ foreach (i, row; maze) foreach (j, col; row) maze[i][j] = n++; ~~~ I like that using 2 variables (i,row) or (j,col) allow to access the variable later as a element in a collection or as an index. It's more flexible. I'll guess I'll need to read more code to avoid programming too much old school (^_^).
Jun 05
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 4 June 2024 at 12:22:23 UTC, Eric P626 wrote:
 I am currently trying to learn how to program in D. I thought 
 that I could start by trying some maze generation algorithms. I 
 have a maze stored as 2D array of structure defined as follow 
 which keep tracks of wall positions:

 ~~~
 struct s_cell
 {
    bool north = true;
    bool east = true;
    bool south = true;
    bool west = true;
 }
 ~~~

 I try to create a 2D array of fixed length and pass it in 
 parameter as a reference. Normally, in C, I would have used a 
 pointer as parameter, and pass the address of the array. Here, 
 I thought it would have been easier just to pass a slice of the 
 array, since a slice is a reference to the original array. So I 
 wrote the signature like this:

 ~~~
 void main()
 {  writeln("Maze generation demo");

    s_cell [5][5] maze;
    print_maze (maze);

 }

 void print_maze ( s_cell [][] maze )
 {
 }
 ~~~

 My idea is that print_maze use a slice of what ever is sent in 
 parameter. Unfortunately, I get the following error message:

 ~~~
 Error: function `mprmaze.print_maze(s_cell[][] maze)` is not 
 callable using argument types `(s_cell[5][5])`
   cannot pass argument `maze` of type `s_cell[5][5]` to 
 parameter `s_cell[][] maze`
 ~~~

 I tried to find a solution on the internet, but could not find 
 anything, I stumble a lot on threads about Go or Rust language 
 even if I specify "d language" in my search.

 Else is there other ways to pass an array as reference using 
 parameter modifiers like: ref,in,out ...

 Else, can it be done the C way using pointers?

 Thank you.
First things first, put ` safe:` on the top of the file or put ` safe` at the end of every function declarator. It makes anything that could be undefined behavior an error: ```d void main() safe { writeln("Maze generation demo"); s_cell [5][5] maze; print_maze (maze); } // change to: ( const ref s_cell [5][5] maze ) void print_maze ( s_cell [][] maze ) safe { } ``` A `T[]` is a pointer–length pair, aka. a slice. A `T[n]` is a block of `n` values of type `T`. Assuming you know a thing or two about C, a `T[n]` converts to a `T[]` like an `int` converts to a `long`: It’s lossless and safe, but not pointer compatible. For the same reason an `int*` can’t convert to a `long*`, a `T[m][n]` can’t convert to a `T[][]`. Also, if you’re new, be aware some people call slices “dynamic arrays,” which is really misleading sometimes. * If a function writes a maze, pass the maze by `ref`. Note that `ref` is not part of the parameter’s type (as in C++), but a property of the parameter akin to its type. * If a function only reads a maze, pass the maze by `const ref`; or `in` using the command-line option `-preview=in` which: allows rvalues and doesn’t bind by reference if the object bound is small and trivial to copy.
Jun 06