Best method? to pass a bidimensional array with known values as a parameter to a function inside a library

Hi everyone. The title pretty much describes it. I cannot figure out for the life of me, how to pass a 2D array as a parameter to a function inside a library. If I pass it with no library, as a normal self contained program, I can do it easily. For example:

#include <Arduino.h>
const int rows = 16;
const int cols = 8;
const int twodmatrix[rows][cols] = {{1,0,0,0,0,0,0,0},       
                                    {0,1,0,0,0,0,0,0},       
                                    {1,1,0,0,0,0,0,0},
                                    {0,0,1,0,0,0,0,0},      
                                    {1,0,1,0,0,0,0,0},       
                                    {0,1,1,0,0,0,0,0},       
                                    {1,1,1,0,0,0,0,0},       
                                    {0,0,0,1,0,0,0,0},
                                    {1,0,0,1,0,0,0,0},
                                    {0,1,0,1,0,0,0,0},
                                    {0,1,1,1,0,0,0,0},
                                    {0,0,1,1,0,0,0,0},
                                    {1,0,1,1,0,0,0,0},
                                    {0,1,1,1,0,0,0,0},
                                    {1,1,1,1,0,0,0,0},
                                    {1,1,1,1,1,1,1,1}};
                         
void setup() {
  Serial.begin(115200);
  delay(3000);
  Serial.println("The results are being displayed next: ");
  BiMatrix();
}

void loop(){}
void BiMatrix(){

     for(int i = 0; i < rows; i++) //x
        {
          for(int j = 0; j < cols; j++) //y
             {
               if((twodmatrix[i][j]) == true)
                 {
                  Serial.print("•  on ");  
                 }
                 else
                   {
                    Serial.print("• off "); //Alt 0149
                   }
             }
             Serial.println();
        }
}

This example compiles perfectly:

The results are being displayed next: 
•  on • off • off • off • off • off • off • off 
• off •  on • off • off • off • off • off • off 
•  on •  on • off • off • off • off • off • off 
• off • off •  on • off • off • off • off • off 
•  on • off •  on • off • off • off • off • off 
• off •  on •  on • off • off • off • off • off 
•  on •  on •  on • off • off • off • off • off 
• off • off • off •  on • off • off • off • off 
•  on • off • off •  on • off • off • off • off 
• off •  on • off •  on • off • off • off • off 
• off •  on •  on •  on • off • off • off • off 
• off • off •  on •  on • off • off • off • off 
•  on • off •  on •  on • off • off • off • off 
• off •  on •  on •  on • off • off • off • off 
•  on •  on •  on •  on • off • off • off • off 
•  on •  on •  on •  on •  on •  on •  on •  on 

I understand that the array will decay as a pointer when passed to a function, and that I really don't even need to pass it as a parameter, asyou can see in the previous example, since it will directly modifiy the array as it is declared globally.
The problem starts if I want the 2D array matrix to be solved inside a function inside a library. I need to pass it as a parameter (along a bunch of other parameters, but I am interested in this one particularly). I have tried several different ways but I keep seeing errors. I am trying here the most straigthforward way (at least for me), but it seems that something is off with the syntax, based on the myriad of errors that keep popping up:

.h

#ifndef  BDMX_TST_H
#define  BDMX_TST_H
#endif
#include <Arduino.h>

class Matrix{

             private:

             public:   void BiMatrix(const int twodmatrix[const int rows_x][const int cols_y]);   
              
};

.cpp

#include <bdmx_tst.h>

void BiMatrix(const int twodmatrix[const int rows][const int cols]){

     for(int i = 0; i < rows; i++) //x
        {
          for(int j = 0; j < cols; j++) //y
             {
               if((twodmatrix[i][j]) == true)
                 {
                  Serial.print("•  on"); 
                 }
                 else
                   {
                    Serial.print("• off");
                   }
             }
             Serial.println();
        }
}

.cpp (user .ino or main)

#include <bdmx_tst.h>

const int rows_x = 16;
const int cols_y = 8;
const int twodmatrix[rows_x][cols_y] = {{1,0,0,0,0,0,0,0},      
                                        {0,1,0,0,0,0,0,0},       
                                        {1,1,0,0,0,0,0,0},
                                        {0,0,1,0,0,0,0,0},       
                                        {1,0,1,0,0,0,0,0},       
                                        {0,1,1,0,0,0,0,0},        
                                        {1,1,1,0,0,0,0,0},      
                                        {0,0,0,1,0,0,0,0},
                                        {1,0,0,1,0,0,0,0},
                                        {0,1,0,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {0,0,1,1,0,0,0,0},
                                        {1,0,1,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {1,1,1,1,0,0,0,0},
                                        {1,1,1,1,1,1,1,1}};     

Matrix matrix;        
         
void setup() {
  Serial.begin(115200);
  Serial.print("The results are being displayed next: ");
  matrix.BiMatrix(twodmatrix);
}

void loop(){}

I have seen various forums, outlining that there are basically 4 different ways, most of them involving pointers, but all of the examples fill the matrix data manualy with an int number (Matrix[] [10] for example) but I want to pass it with const values defined (or is that incorrect?). These are the errors I keep seeing:

bdmx_tst.h:10:59: error: expected primary-expression before 'const'
public: void BiMatrix(const int twodmatrix[const int rows_x][const int cols_y]);
^~~~~
bdmx_tst.h:10:59: error: expected ']' before 'const'
public: void BiMatrix(const int twodmatrix[const int rows_x][const int cols_y]);
^~~~~
]
dmx_tst.h:10:59: error: expected ')' before 'const'
public: void BiMatrix(const int twodmatrix[const int rows_x][const int cols_y]);
~ ^~~~~
)
bdmx_tst.h:10:59: error: expected ';' at end of member declaration
public: void BiMatrix(const int twodmatrix[const int rows_x][const int cols_y]);
^~~~~
;
bdmx_tst.h:10:69: error: expected ';' at end of member declaration
public: void BiMatrix(const int twodmatrix[const int rows_x][const int cols_y]);
^~~~~~
;
bdmx_tst.h:10:75: error: expected unqualified-id before ']' token
public: void BiMatrix(const int twodmatrix[const int rows_x][const int cols_y]);
^
n function 'void setup()':
R003:27:3: error: 'BiMatrix' was not declared in this scope
BiMatrix(twodmatrix);
ino:27:3: note: suggested alternative: 'Matrix'
BiMatrix(twodmatrix);
^~~~~~~~
Matrix

I am probably doing a bunch of things wrong and maybe is something easy which I am overlooking. Any ideas how to achieve this?

Thanks in advance!

You can't because everything that is passed is first moved to an intermediate area, sometimes called a stack. You want to pass the ADDRESS of the array to the function and it can access the array using the address.

You want to pass the ADDRESS of the array to the function and it can access the array using the address.

Can you elaborate further? I am attempting to pass the array with pointers, such as:

user.ino

#include <bdmx_tst.h>

const int rows_x = 16;
const int cols_y = 8;
const int twodmatrix[rows_x][cols_y] = {{1,0,0,0,0,0,0,0},      
                                        {0,1,0,0,0,0,0,0},       
                                        {1,1,0,0,0,0,0,0},
                                        {0,0,1,0,0,0,0,0},       
                                        {1,0,1,0,0,0,0,0},       
                                        {0,1,1,0,0,0,0,0},        
                                        {1,1,1,0,0,0,0,0},      
                                        {0,0,0,1,0,0,0,0},
                                        {1,0,0,1,0,0,0,0},
                                        {0,1,0,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {0,0,1,1,0,0,0,0},
                                        {1,0,1,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {1,1,1,1,0,0,0,0},
                                        {1,1,1,1,1,1,1,1}};     

Matrix matrix;        
         
void setup() {
  Serial.begin(115200);
  Serial.print("The results are being displayed next: ");
  matrix.BiMatrix(*twodmatrix);
}

void loop(){}

.cpp

#include <bdmx_tst.h>

void BiMatrix(const int (*twodmatrix)[const int rows][const int cols]){

     for(int i = 0; i < rows; i++) //x
        {
          for(int j = 0; j < cols; j++) //y
             {
               if((twodmatrix[i][j]) == true)
                 {
                  Serial.print("•  on"); 
                 }
                 else
                   {
                    Serial.print("• off");
                   }
             }
             Serial.println();
        }
}

.h

#ifndef  BDMX_TST_H
#define  BDMX_TST_H
#endif
#include <Arduino.h>

class Matrix{

             private:

             public:   void BiMatrix(const int (*twodmatrix)[const int rows_x][const int cols_y]);   
              
};

But it is still giving me a bunch of syntax errors:

bdmx_tst.h:10:62: error: expected primary-expression before 'const'
public: void BiMatrix(const int (*twodmatrix)[const int rows_x][const int cols_y]);
^~~~~
bdmx_tst.h:10:62: error: expected ']' before 'const'
public: void BiMatrix(const int (*twodmatrix)[const int rows_x][const int cols_y]);
^~~~~
]
bdmx_tst.h:10:62: error: expected ')' before 'const'
public: void BiMatrix(const int (*twodmatrix)[const int rows_x][const int cols_y]);
~ ^~~~~
)
bdmx_tst.h:10:62: error: expected ';' at end of member declaration
public: void BiMatrix(const int (*twodmatrix)[const int rows_x][const int cols_y]);
^~~~~
;
bdmx_tst.h:10:72: error: expected ';' at end of member declaration
public: void BiMatrix(const int (*twodmatrix)[const int rows_x][const int cols_y]);
^~~~~~
;
bdmx_tst.h:10:78: error: expected unqualified-id before ']' token
public: void BiMatrix(const int (*twodmatrix)[const int rows_x][const int cols_y]);
^

I guess I'm not passing the array pointers properly ? any help would be much appreciated.

If the dimensions are known, you may want to use a reference:

void test(int (& arr)[2][2]) {
   // Do something with `arr`.
}

For example,

void test(int (& arr)[2][2]) {
  Serial.println(arr[0][0]);
  Serial.println(arr[1][1]);
}

void setup() {
  Serial.begin(9600);

  int arr[2][2] = {{1, 2}, {3, 4}};
  test(arr);
}

Result:

1
4

And, of course, that could be generalized using a template.

I did not want to start an other code duplication discussion, but indeed, a more generic solution would look something like this:

template <size_t n, size_t m> void test(int (& arr)[n][m]) {
  // Element (i, j) can be accessed as `arr[i][j]`.
}

If code duplication is an issue, then perhaps the following would be a workaround, although it comes at the expense of easy indexing.

void test_(int* arr, size_t n, size_t m) {
  // Element (i, j) can be accessed as `*(arr + i * n + j)`.
}

template <size_t n, size_t m> void test(int (& arr)[n][m]) {
  test_(&arr[0][0], n, m);
}

There's a discussion here arguing that such pointer indexing is invalid (undefined behavior).

Hmm, could be. Accessing the element via the index of an 1D array seems to be UB indeed, but if the memory layout of a multidimensional array is specified in the standard, accessing it via a pointer may not be (&arr[0][0] probably be replaced by reinterpret_cast<int*>(arr) then, although this is also a telltale sign of UB). If this is also not valid, then I guess a series of memcpys are needed before the non-template function should be called.

In any case it is murky at best. Personally I would stick to the template function and not be too bothered by the couple of extra bytes in FLASH memory.

@PieterP usually has some useful insights when it comes to these things. In the mean time I am going to download and compile the standard, it is about time to get this sort of knowledge from the source instead of internet fora, you never know what to believe [1, 2, 3].

[edit]

Maybe something like this is admissible:

void test_(int const arr[], size_t const n, size_t const m) {
  // Element (i, j) can be accessed as `arr[i * n + j]`.
}

template <size_t n, size_t m> void test(int const (& arr)[n][m]) {
  int arr_[n * m];
  memcpy(arr_, arr, n * m * sizeof(int));
  test_(arr_, n, m);
}

I’m old-fashioned so I would write

void BiMatrix(const int rows, const int coils, const int *matrix)

I did not want to start an other code duplication discussion, but indeed, a more generic solution would look something like this:

template <size_t n, size_t m> void test(int (& arr)[n][m]) {
  // Element (i, j) can be accessed as `arr[i][j]`.
}

Ok, so I'm trying to adapt your template example into the library situation, but still I have not been able to declare it properly. I adapted like this:

.h

#ifndef  BDMX_TST_H
#define  BDMX_TST_H
#endif
#include <Arduino.h>

class Matrix{

             private:

           //public:   void BiMatrix(const int (*twodmatrix)[][int cols_y]);
             public:   template <size_t rows, size_t cols> void BiMatrix(const int(& twodmatrix)[const int rows][const int cols]);
              
};

.cpp

#include <bdmx_tst.h>

template <size_t rows, size_t cols> void BiMatrix(const int(& twodmatrix)[const int rows][const int cols]){

     for(int i = 0; i < rows; i++) //x
        {
          for(int j = 0; j < cols; j++) //y
             {
               if((&twodmatrix[i][j]) == true)
                 {
                  Serial.print("•  on"); 
                 }
                 else
                   {
                    Serial.print("• off");
                   }
             }
             Serial.println();
        }
}

user .cpp

#include <bdmx_tst.h>

const int rows = 16;
const int cols = 8;
const int twodmatrix[rows][cols] = {{1,0,0,0,0,0,0,0},      
                                        {0,1,0,0,0,0,0,0},       
                                        {1,1,0,0,0,0,0,0},
                                        {0,0,1,0,0,0,0,0},       
                                        {1,0,1,0,0,0,0,0},       
                                        {0,1,1,0,0,0,0,0},        
                                        {1,1,1,0,0,0,0,0},      
                                        {0,0,0,1,0,0,0,0},
                                        {1,0,0,1,0,0,0,0},
                                        {0,1,0,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {0,0,1,1,0,0,0,0},
                                        {1,0,1,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {1,1,1,1,0,0,0,0},
                                        {1,1,1,1,1,1,1,1}};     

Matrix matrix;        
         
void setup() {
  Serial.begin(115200);
  Serial.print("The results are being displayed next: ");
  matrix.BiMatrix(*twodmatrix);
}

void loop(){}

the errors appearing>:
bdmx_tst.h:11:98: error: expected primary-expression before 'const'
public: template <size_t rows, size_t cols> void BiMatrix(const int(& twodmatrix)[const int rows][const int cols]);
bdmx_tst.h:11:98: error: expected ']' before 'const'
public: template <size_t rows, size_t cols> void BiMatrix(const int(& twodmatrix)[const int rows][const int cols]);
^~~~~
]
bdmx_tst.h:11:98: error: expected ')' before 'const'
public: template <size_t rows, size_t cols> void BiMatrix(const int(& twodmatrix)[const int rows][const int cols]);
~ ^~~~~
)
R003a:27:10: error: 'class Matrix' has no member named 'BiMatrix'; did you mean 'Matrix'?
matrix.BiMatrix(*twodmatrix);
^~~~~~~~
Matrix

Any further insight in the proper declaration of the template in a library context (.h /.cpp / .ino) will be much appreciated.

I'm testing on your old fashioned approach, and so far only one error is being highlighted by the compiler (error at the very bottom of the comment)
.h

#ifndef  BDMX_TST_H
#define  BDMX_TST_H
#endif
#include <Arduino.h>

class Matrix{

             private:

             public:   void BiMatrix(const int rows_x, const int cols_y, const int *twodmatrix);
              
};

.cpp

#include <bdmx_tst.h>

void BiMatrix(const int rows_x, const int cols_y, const int *twodmatrix){

     for(int i = 0; i < rows_x; i++) //x
        {
          for(int j = 0; j < cols_y; j++) //y
             {
               if((twodmatrix[i][j]) == true)
                 {
                  Serial.print("•  on"); 
                 }
                 else
                   {
                    Serial.print("• off");
                   }
             }
             Serial.println();
        }
}

user .cpp (.ino)

#include <bdmx_tst.h>

const int rows_x = 16;
const int cols_y = 8;
const int twodmatrix[rows_x][cols_y] = {{1,0,0,0,0,0,0,0},      
                                        {0,1,0,0,0,0,0,0},       
                                        {1,1,0,0,0,0,0,0},
                                        {0,0,1,0,0,0,0,0},       
                                        {1,0,1,0,0,0,0,0},       
                                        {0,1,1,0,0,0,0,0},        
                                        {1,1,1,0,0,0,0,0},      
                                        {0,0,0,1,0,0,0,0},
                                        {1,0,0,1,0,0,0,0},
                                        {0,1,0,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {0,0,1,1,0,0,0,0},
                                        {1,0,1,1,0,0,0,0},
                                        {0,1,1,1,0,0,0,0},
                                        {1,1,1,1,0,0,0,0},
                                        {1,1,1,1,1,1,1,1}};     

Matrix matrix;        
         
void setup() {
  Serial.begin(115200);
  Serial.print("The results are being displayed next: ");
  matrix.BiMatrix(rows_x, cols_y, *twodmatrix);
}

void loop(){}

The error:
bdmx_tst.cpp: In function 'void BiMatrix(int, int, const int*)':
bdmx_tst.cpp:9:35: error: invalid types 'const int[int]' for array subscript
if((twodmatrix[i][j]) == true)
^

Any further ideas will be much appreciated

This is not what was suggested:

You could try this instead:

template <size_t rows, size_t cols> void BiMatrix(int const (& twodmatrix)[rows][cols]) {

#include <array>

if supported

Inside the function the int * matrix has to be treated as a one-dimensional matrix:

Int X = matrix[i*cols+j];

Think is will work on any 32 bit mcu

@johnwasser, remember This Discussion?

Only trouble with 'std::array' approach is that both the datatype and size are template parameters. Thus they are set at compile time. So that means these two arrays are different data types:

std::array<int, 2> array1;
std::array<int, 100> array2;

So, you can't define (without using templates) a single function that accepts (a reference) to both as an argument.

A 'std::vector' object is different. Its datatype is only defined by the type of objects it holds, not its size. So, you can pass a reference to any std::vector<int> object to a function regardless of its size. But, I don't think there's a 2D vector-type object in the STL.

I don’t see any trouble

Try using your 2 x std::array technique to pass two different 2D arrays that have different sizes to the same function.

Try passing 2 arrays of different sizes to the same function without passing the size or using template