Возможно ли иметь макросы C с каким-либо состоянием? В данном конкретном случае целочисленное значение.
Я хочу иметь возможность выделять некоторую память из статически выделенного буфера, поскольку я нахожусь во встроенной среде. Скажем, у меня есть такая структура и буфер:
double buffer[1000];
typedef struct {
int rows;
int cols;
double* data;
} matrix;
Я хотел бы иметь макрос, который может генерировать матрицу и выделять соответствующую память из рассматриваемого буфера. Однако я не могу придумать, как сохранить внутренний счетчик того, где в буфере мы должны находиться в данный момент. В идеале макрос должен выглядеть примерно так:
#define alloc_matrix(_rows, _cols) {.rows = _rows, .cols = _cols, .data = &buffer[PTR]}
где PTR — это константа времени компиляции, которая постоянно меняется (увеличивается на строки * столбцы) при каждом использовании alloc_matrix. Это позволило бы мне проверить, все ли помещается в буфер во время компиляции, используя что-то вроде _Static_assert, вместо того, чтобы проверять все во время выполнения.
Возможно ли это с препроцессором C?
Я пробовал использовать препроцессор C, но, похоже, не нашел способа сохранить состояние в макросах.
PTR должна быть глобальной переменной.
Нет, у макросов нет своего состояния, но это неправильный вопрос. Ваша программа должна поддерживать принадлежащее ей состояние. Вы можете использовать макросы для создания кода, который обращается к переменным, в которых вы храните это состояние, и изменяет их. Как только вы их определите.
Это возможно во время компиляции в C++.
Однако с учетом вышесказанного вам, вероятно, следует использовать вместо этого функции.
Почему для всех матриц должен быть общий буфер данных? Традиционным способом было бы объявить две статические переменные для каждой матрицы отдельно: одну для данных матрицы и одну для структуры матрицы. Не надо никакой макро-ерунды.
Задача компоновщика — выделить место для ваших переменных и убедиться, что они помещаются в нужные разделы. Можете ли вы просто объявить глобальные переменные для всех данных матрицы отдельно и использовать компоновщик для их упорядочения? Кроме того, пока не думайте о макросах, пока не научитесь писать правильный код. Затем используйте макросы, чтобы сделать код кратким, если это необходимо.
проблема X-Y наверняка





Если вы готовы отказаться от требования, чтобы все матрицы использовали общий буфер данных, вы можете использовать здесь составные литералы:
#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);
Но помните, что если вам нужна статическая продолжительность хранения, то это работает только вне функций; когда составной литерал определен в функции, он будет иметь автоматическую продолжительность хранения.
Небольшое примечание: стандарт C23 допускает static продолжительность хранения составных литералов.
Это возможно, если вы мыслите нестандартно. Почему вы должны выделять фрагменты, указывающие на заранее выделенный массив?
Вместо этого рассмотрите возможность создания структуры, содержащей только double элементы массива. Такая структура не будет иметь проблем с заполнением или выравниванием. Он будет увеличиваться в размерах по мере добавления новых членов, и все это во время компиляции.
Используя печально известные, но до смешного гибкие «X-макросы», мы можем объявить все наши матрицы/массивы в абстрактном списке:
#define DATA_LIST(X) \
/* name rows cols */ \
X(data1, 5, 7) \
X(data2, 2, 2) \
X(data42, 42, 42) \
Теперь создайте тип структуры на основе этого:
typedef struct
{
#define MATRIX_MEMBER_DECL(name, rows, cols) double name [rows][cols];
DATA_LIST(MATRIX_MEMBER_DECL)
} matrix_t;
Это распространяется на:
typedef struct
{
double data1 [5][7];
double data2 [2][2];
double data42 [42][42];
} matrix_t;
Экземпляр этой структуры займет именно этот объем, ни больше, ни меньше. Нам не нужно заранее выделять произвольный фрагмент и пытаться назначить на него указатели.
matrix_t matrix;
printf("double items: %zu\n", sizeof matrix / sizeof(double));
printf("Total size: %zu\n", sizeof matrix);
Это дает:
double items: 1803
Total size: 14424
(1803 * 8 = 14424, поэтому не было ожидаемого заполнения)
Вы можете получить доступ к отдельным матрицам/массивам, как к любому члену структуры:
matrix.data42[0][0] = 3.1415;
Хотите узнать размер строки или столбца data42? Создайте соответствующее перечисление для поиска:
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
И так далее. Это все 100% чистые вычисления во время компиляции.
Добавьте глобальный счетчик и увеличивайте его при каждом вызове макроса.