Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

macro for simulating access two dimensional array in C

OpenCL only offers access to single dimensional arrays using the C99 specs. My problem however is in two dimensions and I am using two dimensional arrays on the host side

Rather than making my code less readable by calculating indices, I would like to use a C macro to get element A[i][j]. Unfortunately I'm rather bad at this and have little experience in C. I think I have the general idea of how it is done, but if someone could critique it would be appreciated.

It would be something like:

#define 2d_access(u, y, x) (u[y][x])

where u is the matrix, y is the row, and x is the column and the macro would return the value at u[y][x]

The matrix is allocated statically so the macro would have a WIDTH component.

#define 2d_access(u, y, x) (u[y * WIDTH] + x])
like image 352
Robert Avatar asked Aug 19 '11 22:08

Robert


2 Answers

Since all the answers so far rely on a constant width, here's a (heavyweight) solution for arbitrary width columns:

#define matrix_type(t) struct { size_t width; t array[]; }

#define matrix_alloc(t, w, h) malloc(offsetof(matrix_type(t), array[(w) * (h)]))

#define matrix_init(m, t, w, h) \
  matrix_type(t) *m = matrix_alloc(t, w, h); \
  if(!m) matrix_alloc_error(); else m->width = (w);

#define matrix_index(m, w, h) m->array[m->width * (w) + (h)]

// redefine if you want to handle malloc errors
#define matrix_alloc_error()

Just deallocate the array with free.

Of course, you can add a field for height as well and do bounds checking, among other things. You could even write these as actual functions, or use a macro to automatically declare struct types so you don't have to use an anonymous struct type for everything. If you need it on the stack, you could use alloca at the cost of portability.

If you have a constant matrix size, you can use some casting hacks to achieve "native" 2D indexing (via the [] operator):

#define CAT_(x, y) x##y
#define CAT(x, y) CAT_(x, y)

#define MANGLE(x) CAT(x, _hidden_do_not_use_0xdeadbeef_)

#define matrix_init(m, t, w, h) \
  t MANGLE(m)[(w) * (h)]; \
  t (*m)[(w)] = (void *)MANGLE(m);

// because of the funky typing, `m[0][1]` does what you'd expect it to.

Note that, unlike the other solution, this creates a second variable, which is probably not very clean, but I think I used a pretty clear mangling method so it won't get in the way in practice.

like image 82
Chris Lutz Avatar answered Nov 05 '22 21:11

Chris Lutz


Even cleaner would be to define a macro for each array you're using, so you can make it look exactly like a 2D array access. So, given the array A, you would define:

#define A(r, c) (A[(r)*WIDTH + (c)])

Note the parentheses around the substituted values. This handles cases where the substitution is an expression, like A(i + 1, j). Without the parentheses, this would expand to A[i + 1*WIDTH + j], which is not what you want:

A[i + 1*WIDTH + j] = A[WIDTH + i + j] != A[(i + 1)*WIDTH + j]

In order to avoid the same problem with the second argument, both are enclosed in parentheses in the substitution text.

like image 28
Jeremy W. Sherman Avatar answered Nov 05 '22 21:11

Jeremy W. Sherman