Function Using an Array defined via a Variable

I am trying to create a function that will allow the use of variable sized multi dimensional arrays, but I'm having trouble wrapping my head around why something isn't working.

I don't think I need to paste the entire code, as it is a lot of repeating declarations, but I have a series of 1 dimensional arrays that store the colors to change LEDs to.

const long PupilDownLeft[] PROGMEM = 
{
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 
0x000000, 0x000000, 0x000000, 0x00a2e8, 0x00a2e8, 0x00a2e8, 
0x000000, 0x000000, 0x000000, 0x00a2e8, 0xed1c24, 0xed1c24, 
0x000000, 0x000000, 0x000000, 0x00a2e8, 0xed1c24, 0xed1c24
}

These are stored as pointers in another array. These can be variable sizes, depending on the length of the animation.

const long* BlinkAnim[][3] PROGMEM =
{
  {PupilCenter, PupilClosed, PupilCenter}, //RightEye
  {PupilCenter, PupilClosed, PupilCenter}, //LeftEye
  {RightEyeOpen, RightEyeClosed, RightEyeOpen},
  {LeftEyeOpen, LeftEyeClosed, LeftEyeOpen}, 
  {RightBrow, RightBrowDown, RightBrow},
  {LeftBrow, LeftBrowDown, LeftBrow},
};

The function declaration is currently this, which works as is.

void animDisplay(int animLen, int delayTimer, const long* anim[][3]) 

And it's called like this in the loop();

animDisplay(3, 300, BlinkAnim);

The issue arises if I want to use an array of a different size. For example, I have an array that has 10 items in each row.

const long* RollAnim[][10]

It works if I change the 3 to a 10 in the function declaration, but I wanted to make it dynamic.

I have tried to assign the length of the array to a variable outside the function (int animSize), and modified the function to be

void animDisplay(int animLen, int delayTimer, const long* anim[][animSize]) 

But I get a couple errors when I try to do that.

Individual_Eye_Test:298:59: error: expected ',' or '...' before 'anim'
 void animDisplay(int animLen, int delayTimer, const long* anim[][animSize]){

and

In function 'void loop()':
Individual_Eye_Test:292:30: error: cannot convert 'const long int* (*)[3]' to 'const long int*'
 for argument '3' to 'void animDisplay(int, int, const long int*)'
 animDisplay(3, 300, BlinkAnim);

The same error also happens if I try to use the animLen variable that I declare in the function.

Is there any way to do what I'm wanting to do? Or would I have to make a new animDispay function for each possible size of array?

C++ implements "row major" storage for arrays therefore must know the number of "columns" at compile time in order to know how to increment to the next row in memory.

You could actually handle this in your code by passing a long** to the function along with the number of rows and the number of columns. That way you can access the array by incrementing the next row yourself and then processing each column.

Don't forget you will have to use special functions to access PROGMEM. PROGMEM

Pass the pointer to the 2D array and the size of the rightmost entry as parameters. Then in the code do the math to go to the correct entry

int v = A[x][y]; is the same as int v = *(A + x*size + y); where size is 3 or 10. Of course x should be within the bounds too.

As @J-M-L said make sure the row parameter stays in bounds. When I pass an array pointer to a function I always like to pass the size that way I can check the bounds.

Sorry, I think there might have been a miscommunication. The issue is I want to be able to pass arrays of different sizes into the function and have the function work. The code inside the function works as long as the passed array is the exact same size as the declaration says

With the function

void animDisplay(int animLen, int delayTimer, const long* anim[][10])

I can call the function with

animDisplay(3, 300, animX);

and it works as long as animX is

const long* animX[][10] PROGMEM

But I would like to use the same function with animY, which is

const long* animY[][3] PROGMEM

But if I try to do that, it errors out because the array can't be converted to a different size.

C++ implements "row major" storage for arrays therefore must know the number of "columns" at compile time in order to know how to increment to the next row in memory.

If the size of the array must be declared with the function at compile time, then it seems I can't do what I'm trying, as I would like to have the function declared array be a variable size depending on what I want to pass to it.

Understood. @J-M-L and I are basically offering the same solution just said in a different way.

@ToddL1962

Can you give an example of what your solution is, because maybe I'm misunderstanding.

You could actually handle this in your code by passing a long** to the function along with the number of rows and the number of columns.

What is in the long**?

I have a series of arrays

const long frame1a[] PROGMEM
const long frame1b[] PROGMEM
const long frame1c[] PROGMEM
const long frame1d[] PROGMEM
const long frame2a[] PROGMEM
const long frame2b[] PROGMEM
const long frame2c[] PROGMEM
const long frame2d[] PROGMEM
const long frame3a[] PROGMEM
const long frame3b[] PROGMEM
const long frame3c[] PROGMEM
const long frame3d[] PROGMEM

These are compiled in a long* as a 2D array of pointers

const long* animX[][3] PROGMEM = {
{frame1a, frame1b,frame1c},
{frame2a, frame2b,frame2c},
{frame3a, frame3b,frame3c}}

const long* animY[][4] PROGMEM = {
{frame1b,frame1c, frame1d, frame1a},
{frame2b, frame2c,frame2c, frame 2a},
{frame3b, frame3c,frame3d, frame3a}}

Do I make a long** with the two long* arrays in them? Because when I try, it fails, unless I'm declaring the long ** incorrectly.

consider,

void someoddfunction( int rows, int anarraywithstuff[] )

How many different-sized arrays do you foresee using? Since you're using PROGMEM, you obviously know that a compile time. Depending on that answer, C++ templates might be a possibility.

without using templates, you could do something like this:

it's pretty convoluted as data is in PROGMEM so you need to extract the right information and in C++ you cannot get directly a 2D array name as the pointer to the first element (type won't match) so there is a bit of a hack forcing the name of the array into a (const long ** const)... But if you get what the compiler is doing with arrays and memory layout, that hack works with our compiler.

const long a1[] PROGMEM = {1, 1, 1, 1};
const long a2[] PROGMEM = {2, 2, 2, 2};
const long a3[] PROGMEM = {3, 3, 3, 3};
const long a4[] PROGMEM = {4, 4, 4, 4};

const byte unitCount = sizeof a1 / sizeof * a1;

const long* const animX3[][3] PROGMEM = {
  {a1, a2, a3},
  {a1, a4, a2},
  {a4, a2, a1},
  {a3, a1, a4},
};

const long* const animX5[][5] PROGMEM = {
  {a1, a2, a3, a2, a4},
  {a1, a4, a2, a1, a2},
  {a4, a2, a1, a3, a1},
  {a3, a1, a4, a1, a2},
  {a1, a2, a3, a4, a1},
  {a3, a2, a1, a1, a2},
};

#define PRINT_ANIM(A) printAnim((const long ** const) &A,  sizeof A / sizeof * A, sizeof A[0] / sizeof * A[0])

void printAnim(const long** const anAnimation, size_t nbRows, size_t nbCols) {
  for (size_t r = 0; r < nbRows; r++) {
    for (size_t c = 0; c < nbCols; c++) {
      const long* animPtr = pgm_read_ptr_near((anAnimation + r * nbCols + c)); // extract the pointer at anAnimation[r][c]
      Serial.print("{ ");
      for (size_t a = 0; a < unitCount; a++) {
        Serial.print(pgm_read_dword_near(&(animPtr[a])));
        Serial.write(' ');
      }
      Serial.print("} ");
    }
    Serial.println();
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200); Serial.println();
  Serial.println("animX3");
  PRINT_ANIM(animX3);
  Serial.println("animX5");
  PRINT_ANIM(animX5);
}

void loop() {}

There is a PRINT_ANIM() macro to make it easier to call the function with the 2 row and cols size without having to write yourself the values. The compiler will generate that for you.

if all goes well, you should see in the Serial monitor at 115200 bauds

animX3
{ 1 1 1 1 } { 2 2 2 2 } { 3 3 3 3 } 
{ 1 1 1 1 } { 4 4 4 4 } { 2 2 2 2 } 
{ 4 4 4 4 } { 2 2 2 2 } { 1 1 1 1 } 
{ 3 3 3 3 } { 1 1 1 1 } { 4 4 4 4 } 

animX5
{ 1 1 1 1 } { 2 2 2 2 } { 3 3 3 3 } { 2 2 2 2 } { 4 4 4 4 } 
{ 1 1 1 1 } { 4 4 4 4 } { 2 2 2 2 } { 1 1 1 1 } { 2 2 2 2 } 
{ 4 4 4 4 } { 2 2 2 2 } { 1 1 1 1 } { 3 3 3 3 } { 1 1 1 1 } 
{ 3 3 3 3 } { 1 1 1 1 } { 4 4 4 4 } { 1 1 1 1 } { 2 2 2 2 } 
{ 1 1 1 1 } { 2 2 2 2 } { 3 3 3 3 } { 4 4 4 4 } { 1 1 1 1 } 
{ 3 3 3 3 } { 2 2 2 2 } { 1 1 1 1 } { 1 1 1 1 } { 2 2 2 2 } 

which is basically a dump of your 2 animation arrays, of various size.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.