Есть ли способ заставить прекомпилятор C использовать математический результат при объединении символов?

У меня есть макрос C, который объединяет биты для преобразования в другие макросы. Я продемонстрирую это с помощью заведомо бессмысленного макроса MULT, который умножает два числа. Пока я передаю фактическое целое число в его первый параметр, это работает. Но передача математического выражения вызывает ошибку.

Например:

#define MULT_2(X) X+X
#define MULT_3(X) X+X+X
#define MULT_4(X) X+X+X+X

#define EXPAND_(A) A
#define MULT__(N, X) EXPAND_(MULT_ ## N)(X)
#define MULT_(N, X) MULT__(N, X)
#define MULT(N, X) MULT_(EXPAND_(N), X)

int main() {
    int num;

    // This works:
    num = MULT(4, 1);
    printf("num: %d\n", num);

    // This does not:
    num = MULT(2+2, 1);
    printf("num: %d\n", num);

    return 0;
}

(Честно говоря, я не до конца понимаю, почему для того, чтобы этот макрос работал только с целым числом, требуются три дополнительных макроса: MULT_, MULT__ и EXPAND_. Но несмотря ни на что...)

Можно ли как-нибудь переписать это так, чтобы передача выражения типа 2+2 или 1 * 4 или 1 << 2 в его первый аргумент действительно работала?

Мне интересен ответ главным образом для того, чтобы расширить мои знания о C и его препроцессоре. Но я также надеюсь использовать это для проекта, написанного на C89, что означает отсутствие переменных макросов, поэтому, если есть решение, позволяющее избежать их использования, я бы хотел его увидеть!

Почему макросы? Почему бы не просто (constexpr) функции?

Jesper Juhl 14.06.2024 21:39

@JesperJuhl Потому что я хочу использовать это в C, где нет constexpr, и потому что я хочу посмотреть, смогу ли я расширить свои знания о макросах C, и возможность сделать это позволит мне писать более мощные макросы.

Bri Bri 14.06.2024 21:42

хорошо, тогда я могу удалить тег C++

Jean-François Fabre 14.06.2024 21:42

«расширить свои знания о макросах C, и возможность сделать это позволит мне писать более мощные макросы». Честно говоря, макросы — это то, чего вам следует стараться избегать, а не то, в чем вы должны стараться стать лучше.

Jesper Juhl 14.06.2024 21:43

тогда было бы гораздо лучше использовать встроенные функции

Jean-François Fabre 14.06.2024 21:43

как только вы используете конкатенацию, как в MULT_ ## N, тогда N ДОЛЖЕН содержать только буквы, цифры и символы подчеркивания (и, возможно, $). Итак, вы хотите сделать что-то невозможное.

Jean-François Fabre 14.06.2024 21:46

@Jean-FrançoisFabre Если это так, то отправьте это в качестве ответа. «Это невозможно», безусловно, является правильным ответом на этот вопрос.

Bri Bri 14.06.2024 22:11
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
7
100
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Это, по сути, невозможно. Конкатенация макросов C (##) не оценивает выражения, а только объединяет токены, как следует из названия.

Вот что расширяется созданный вами макрос при попытке добавить математическое выражение:

#define MULT_2(X) X + X
#define MULT_3(X) X + X + X
#define MULT_4(X) X + X + X + X

#define EXPAND_(A)   A
#define MULT__(N, X) EXPAND_(MULT_##N)(X)
#define MULT_(N, X)  MULT__(N, X)
#define MULT(N, X)   MULT_(EXPAND_(N), X)

// MULT(2+2, 1)
MULT_2 + 2(1)

Это явно не желаемый эффект.

Конкатенация с помощью ## объединяет токены в новый токен предварительной обработки. Создание строкового литерала — это другой оператор, #.

Eric Postpischil 14.06.2024 23:23

Есть ли способ заставить прекомпилятор C использовать математический результат при объединении символов?

Да, но вам придется выполнять арифметику в препроцессоре, а не вне его, а затем использовать вычисленный препроцессором результат для выполнения операции. Это выглядело бы примерно так:

#include <stdio.h>

#define ADD_1_1  2
#define ADD_1_2  3
#define ADD_2_2  4
/* etc, bilions of combinations */
#define ADD(a, b)  ADD_##a##_##b

#define MULT_2(X) X+X
#define MULT_3(X) X+X+X
#define MULT_4(X) X+X+X+X

#define EXPAND_(A) A
#define MULT__(N, X) EXPAND_(MULT_ ## N)(X)
#define MULT_(N, X) MULT__(N, X)
#define MULT(N, X) MULT_(EXPAND_(N), X)

int main() {
    int num;
    num = MULT(4, 1);
    printf("num: %d\n", num);
    num = MULT(ADD(2, 2), 1);
    printf("num: %d\n", num);
}

Возможно, вас заинтересуют библиотеки boost BOOST_PP_ADD и BOOST_PP_MUL, а также библиотека P99 P99_ADD и P99_MUL (используйте boost).

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