Избегайте дублирования кода с помощью средств для вставки токенов C

Вот как выглядит мой макрос #define:

#define CUSTOM_COMPARATOR(table, column, a, b)                          \
    do {                                                                \
        const struct db_##table *row1 = a;                              \
        const struct db_##table *row2 = b;                              \
                                                                        \
        /* Insert lot of code here with multiple row->##column usage */ \
        return strcmp(row1->##column->name1, row2->##column->name2);    \
    } while (0)

Мне нужно создать несколько определений на основе разных типов таблиц и столбцов. Эти определения предварительно обрабатываются и вызываются в нескольких функциях:

static int
db_table_sometable_somecolumn_comparator(const void *a, const void *b) {
    CUSTOM_COMPARATOR(sometable, somecolumn, a, b);
}

static int
db_table_someothertable_someothercolumn_comparator(const void *a, const void *b) {
    CUSTOM_COMPARATOR(someothertable, someothercolumn, a, b);
}

По сути, я хочу избежать дублирования кода внутри CUSTOM_COMPARATOR. Когда я компилирую этот gcc, он жалуется, что pasting "->" and "somecolumn" does not give a valid preprocessing token, что понятно, поскольку -> ломает токен. Он работает на db_##table, потому что производит один токен.

Тем не менее, есть ли способ добиться этого? У меня есть около 10 таблиц/столбцов с простым изменением имени, но я использую ту же логику, что и в CUSTOM_COMPARATOR, что в действительности также составляет 50 LoC.

Меня удивляет, что в структуре name1 есть два члена, name2 и struct db_tablename. Я ожидаю, что вы будете использовать один столбец name.

Jonathan Leffler 09.07.2024 22:01

Вы правы, на эту фразу больно смотреть. Это действительно просто name. Я опечатал его, пытаясь переименовать производственный код.

tcpip 09.07.2024 22:11

Почему бы и нет #define GENERATE_COMPARATOR(table, column) static int db_table_ ## table ## _ ## column ## _comparator(const void *a, const void *b) { CUSTOM_COMPARATOR(table, column, a, b) }? Это снижает риск несоответствия имени функции и кода компаратора.

Jonathan Leffler 09.07.2024 22:26

И вам может понадобиться #define COMPARATOR_NAME(table, column) db_table_ ## table ## _ ## column ## _comparator для создания имени компаратора. Вам следует использовать это в определении GENERATE_COMPARATOR, но вы также можете использовать его при создании имени для передачи в код сортировки: qsort(rows, number, sizeof(struct db_sometable), COMPARATOR_NAME(sometable, somecolumn)); и т. д. Вы можете даже беспокоиться о заблокированном sometable в db_sometable. Вы можете сойти с ума, если не будете осторожны.

Jonathan Leffler 09.07.2024 22:31

Вставку токенов следует рассматривать как дополнительную функцию. Вы можете написать множество полезных макросов без вставки токенов. Если вы планируете использовать его, помните, что полезная вставка токена почти всегда принимает форму соединения двух частей вместе для формирования идентификатора (имени). Если результат вставки не предназначен для использования в качестве идентификатора, то, вероятно, вы не хотите выполнять эту вставку.

John Bollinger 09.07.2024 22:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не пытайтесь вставить имя столбца, чтобы создать новый токен. Вы можете просто сделать это:

        return strcmp(row1->column->name1, row2->column->name2);

Это принимается препроцессором и расширяется, например, до с gcc -E:

static int
db_table_sometable_somecolumn_comparator(const void *a, const void *b) {
    do { const struct db_sometable *row1 = a; const struct db_sometable *row2 = b; return strcmp(row1->somecolumn->name1, row2->somecolumn->name2); } while (0);
}

static int
db_table_someothertable_someothercolumn_comparator(const void *a, const void *b) {
    do { const struct db_someothertable *row1 = a; const struct db_someothertable *row2 = b; return strcmp(row1->someothercolumn->name1, row2->someothercolumn->name2); } while (0);
}

что совершенно справедливо C.

Это работает. Большое спасибо!

tcpip 09.07.2024 21:40

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