Hi guys,
I'm trying to implement a function to do some 2D / bilinear interpolation for me. It will be a function that accepts a 2D table as a parameter to lookup from. I've got all the interpolation maths under control. But I'm struggling with passing the table as a parameter to the function. The function needs to accept all different sizes of tables.
This is what I've boiled it down to after chasing the problem:
In this example I'm expecting to get "5" sent back on the serial monitor, but I get "0".
Originally I tried using the function in loop by simply:
uint16_t value = useThatTableForSomething(some2Dtable);
But received a complier error:
error: cannot convert 'uint16_t (*)[4] {aka unsigned int (*)[4]}' to 'uint16_t** {aka unsigned int**}' for argument '1' to 'uint16_t useThatTableForSomething(uint16_t**)
What am I doing wrong? I'm fairly sure its something wrong with the way I'm passing the table to the function, but I don't understand why.
If you call the function with many different sizes, this might generate a lot of code though, in which case you could consider passing the tables as a struct, for example:
Note that when array-to-pointer decay is applied, a multidimensional array is converted to a pointer to its first element (e.g., a pointer to its first row or to its first plane): array-to-pointer decay is applied only once.
Unfortunately, this invokes undefined behavior: you're only allowed to index from table[0] through table[width] and dereference from 0 to width-1.
This is because some2Dtable[0] is an array of length width, so anything beyond that would be out of bounds. Or put another way, table[n] is equivalent to some2Dtable[0][n] which is only allowed for 0 <= n <= width (or 0 <= n < width if you use the result).
AFAIK, it doesn't matter that the array is part of a multidimensional array.
I don't know of an alternative, though, except for: don't use multidimensional arrays if you need to do things like this.
Instead, allocate a one-dimensional array and index it yourself, preferably through a class that supports the matrix[row][col] or matrix(row, col) syntax, using a tested matrix or tensor library.
It just doesn't say that that's the case, so you cannot assume it is.
About multidimensional arrays, it just says [dcl.array]:
[Note 3 : When several “array of” specifications are adjacent, a multidimensional array type is created; only the first of the constant expressions that specify the bounds of the arrays can be omitted.
[Example 4 :
int x3d[3][5][7];
declares an array of three elements, each of which is an array of five elements, each of which is an array of seven integers. The overall array can be viewed as a three-dimensional array of integers, with rank 3 × 5 × 7. Any of the expressions x3d, x3d[i], x3d[i][j], x3d[i][j][k] can reasonably appear in an expression. The expression x3d[i] is equivalent to *(x3d + i); in that expression, x3d is subject to the array-to-pointer conversion (7.3.3) and is first converted to a pointer to a 2-dimensional array with rank 5 × 7 that points to the first element of x3d. Then i is added, which on typical implementations involves multiplying i by the length of the object to which the pointer points, which is sizeof(int)×5 × 7. The result of the addition and indirection is an lvalue denoting the i th array element of x3d (an array of five arrays of seven integers). If there is another subscript, the same argument applies again, so x3d[i][j] is an lvalue denoting the j th array element of the i th array element of x3d (an array of seven integers), and x3d[i][j][k] is an lvalue denoting the k th array element of the j th array element of the i th array element of x3d (an integer). — end example]
The first subscript in the declaration helps determine the amount of storage consumed by an array but plays no other part in subscript calculations. — end note]
Recall that the built-in subscript operator is just syntactic sugar for pointer arithmetic, i.e. a[b] == *(a + b), about which the standard says:
When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.
(4.1) — If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.
(4.2) — Otherwise, if P points to an array element i of an array object x with n elements (9.3.4.5), the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element i + j of x if 0 ≤ i + j ≤ n and the expression P - J points to the (possibly-hypothetical) array element i − j of x if 0 ≤ i − j ≤ n.
(4.3) — Otherwise, the behavior is undefined.
using TwoDimArray = std::array<std::array<uint16_t, NCOLS>, NROW>;
would be a place to start for this. Unfortunately, the size of a std:array is part of its type. So, you can’t call a common function with different array sizes without using templates (in which case it’s not really a common function).
Ideally, you could wrap the 2-Dim std:array composite in a common container class that also keeps track of the dimensions. Then a 2D array of any size could be passed to a common function. Unfortunately, my C++ skills at creating STL-like containers are non-existent.
EDIT:
Ooops ... never mind ... won't work. std::array doesn't use dynamic storage. So, can't make common class as different array sizes would mean different object sizes. std::vector maybe.