C macros with states

149 Views Asked by At

Is it possible to have C macros with any sort of state? In this particular case, an integer value.

I want to be able to allocate some memory from statically allocated buffer, since I am in an embedded environment. Say I have a struct like this and a buffer:

double buffer[1000];

typedef struct {
    int rows;
    int cols;
    double* data;
} matrix;

I would like to have macro that can generate a matrix and allocate the relevant memory from the buffer in question. However, I cannot seem to think of a way to keep an internal counter of where in the buffer we should be at the moment. Ideally, the macro would be something like:

#define alloc_matrix(_rows, _cols) {.rows = _rows, .cols = _cols, .data = &buffer[PTR]}

where PTR is compile time constant that is constantly changing (increasing by rows * cols) for every usage of alloc_matrix. This would allow me check if everything fits the buffer during compile time using something like _Static_assert, instead of checking everything during runtime.

Is this possible with the C preprocessor?

I have tried using the C preprocessor but can't seems to find a way to store a state in the macros.

2

There are 2 best solutions below

0
Lundin On BEST ANSWER

It is possible if you think outside the box. Why must you be allocating chunks pointing into a pre-allocated array?

Instead, consider making a struct containing nothing but double array members. Such a struct wouldn't have any padding or alignment problems. It will grow in size as you add more members, all at compile-time.

Rolling out the infamous but ridiculously flexible "X macros", we can declare all our matrices/arrays in an abstract list:

#define DATA_LIST(X)        \
/*  name    rows cols */    \
  X(data1,  5,   7)         \
  X(data2,  2,   2)         \
  X(data42, 42, 42)         \

Now create a struct type based on this:

typedef struct
{
  #define MATRIX_MEMBER_DECL(name, rows, cols) double name [rows][cols];
  DATA_LIST(MATRIX_MEMBER_DECL)
} matrix_t;

This expands to:

typedef struct
{
  double data1 [5][7];
  double data2 [2][2];
  double data42 [42][42];
} matrix_t;

An instance of this struct will take up exactly that amount, no more, no less. We need not pre-allocate an arbitrary chunk and try to assign pointers into it.

matrix_t matrix;
printf("double items:  %zu\n", sizeof matrix / sizeof(double));
printf("Total size:    %zu\n", sizeof matrix);

This gives:

double items:  1803
Total size:    14424

(1803 * 8 = 14424, so there was no padding as predicted)

You may access individual matrices/arrays like you would any struct member:

matrix.data42[0][0] = 3.1415;

Wanna know the row or column size of data42? Make a corresponding enum for look-up:

typedef enum
{
  #define MATRIX_MEMBER_ROWS(name, rows, cols) name##_##row = rows,
  DATA_LIST(MATRIX_MEMBER_ROWS)
} matrix_row_t;

#define MATRIX_GET_ROWS(name) name##_##row

...
printf("%d\n", MATRIX_GET_ROWS(data42)); // prints 42

And so on. This is all 100% pure compile-time calculations.

1
user694733 On

If you are willing to give up the requirement that all matrices use shared data buffer, you could use compound literals here:

#define alloc_matrix(r, c) {.rows = (r), .cols = (c), .data = (double[(r)*(c)]){0}}

// Allocate immutable matrix with mutable data space
static const matrix MyMatrix = alloc_matrix(3, 4);

But please remember that if you want static storage duration, then this works only outside functions; when compound literal is defined in function, it will have automatic storage duration.