У меня есть макрос 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, что означает отсутствие переменных макросов, поэтому, если есть решение, позволяющее избежать их использования, я бы хотел его увидеть!
@JesperJuhl Потому что я хочу использовать это в C, где нет constexpr
, и потому что я хочу посмотреть, смогу ли я расширить свои знания о макросах C, и возможность сделать это позволит мне писать более мощные макросы.
хорошо, тогда я могу удалить тег C++
«расширить свои знания о макросах C, и возможность сделать это позволит мне писать более мощные макросы». Честно говоря, макросы — это то, чего вам следует стараться избегать, а не то, в чем вы должны стараться стать лучше.
тогда было бы гораздо лучше использовать встроенные функции
как только вы используете конкатенацию, как в MULT_ ## N
, тогда N
ДОЛЖЕН содержать только буквы, цифры и символы подчеркивания (и, возможно, $). Итак, вы хотите сделать что-то невозможное.
@Jean-FrançoisFabre Если это так, то отправьте это в качестве ответа. «Это невозможно», безусловно, является правильным ответом на этот вопрос.
Это, по сути, невозможно. Конкатенация макросов 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)
Это явно не желаемый эффект.
Конкатенация с помощью ##
объединяет токены в новый токен предварительной обработки. Создание строкового литерала — это другой оператор, #
.
Есть ли способ заставить прекомпилятор 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).
Почему макросы? Почему бы не просто (
constexpr
) функции?