Макросы C с состояниями

Возможно ли иметь макросы 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, но, похоже, не нашел способа сохранить состояние в макросах.

Добавьте глобальный счетчик и увеличивайте его при каждом вызове макроса.

Eugene Sh. 27.02.2024 16:18
PTR должна быть глобальной переменной.
Ted Lyngmo 27.02.2024 16:18

Нет, у макросов нет своего состояния, но это неправильный вопрос. Ваша программа должна поддерживать принадлежащее ей состояние. Вы можете использовать макросы для создания кода, который обращается к переменным, в которых вы храните это состояние, и изменяет их. Как только вы их определите.

John Bollinger 27.02.2024 16:18

Это возможно во время компиляции в C++.

HolyBlackCat 27.02.2024 16:18

Однако с учетом вышесказанного вам, вероятно, следует использовать вместо этого функции.

John Bollinger 27.02.2024 16:19

Почему для всех матриц должен быть общий буфер данных? Традиционным способом было бы объявить две статические переменные для каждой матрицы отдельно: одну для данных матрицы и одну для структуры матрицы. Не надо никакой макро-ерунды.

user694733 27.02.2024 16:21

Задача компоновщика — выделить место для ваших переменных и убедиться, что они помещаются в нужные разделы. Можете ли вы просто объявить глобальные переменные для всех данных матрицы отдельно и использовать компоновщик для их упорядочения? Кроме того, пока не думайте о макросах, пока не научитесь писать правильный код. Затем используйте макросы, чтобы сделать код кратким, если это необходимо.

David Grayson 27.02.2024 16:45

проблема X-Y наверняка

gulpr 27.02.2024 18:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
170
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы готовы отказаться от требования, чтобы все матрицы использовали общий буфер данных, вы можете использовать здесь составные литералы:

#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 продолжительность хранения составных литералов.

tstanisl 28.02.2024 08:22
Ответ принят как подходящий

Это возможно, если вы мыслите нестандартно. Почему вы должны выделять фрагменты, указывающие на заранее выделенный массив?

Вместо этого рассмотрите возможность создания структуры, содержащей только 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% чистые вычисления во время компиляции.

Другие вопросы по теме