C Препроцессор условно с расширенными макросами и типами
Для микроконтроллера я использую макросы для HAL. Теперь, чтобы обобщить использование HAL, я хочу сделать что-то вроде
#define UART UART1
#if UART==UART1
# define PIN_TX 9
#elif UART==UART2
# define PIN_TX 2
#else
# warning "UART not correctly defined"
#endif
Тем не менее, UART1 является адресом памяти с приведением типа (например, (uint8_t*)0x004000000
). Таким образом, компилятор печатает некоторые ошибки.
Я сделал простой пример:
#include <stdio.h>
#define v1 (double)1
#define v2 (double)2
int main(int argc, char *argv[])
{
printf("We have: ");
#define VAL (v1)
#if VAL==v1
printf("VAL is 1\n");
#elif VAL==v2
printf("VAL is 2\n");
#else
# warning "VAL not 1 or 2"
printf("Not defined\n");
#endif
}
Который также не скомпилируется с gcc со следующими комментариями:
cc preproc.c -o preproc
preproc.c: In function ‘main’:
preproc.c:3:20: error: missing binary operator before token "1"
#define v1 (double)1
^
preproc.c:10:14: note: in expansion of macro ‘v1’
#define VAL (v1)
^~
preproc.c:12:5: note: in expansion of macro ‘VAL’
#if VAL==v1
^~~
preproc.c:3:20: error: missing binary operator before token "1"
#define v1 (double)1
^
preproc.c:10:14: note: in expansion of macro ‘v1’
#define VAL (v1)
^~
preproc.c:14:7: note: in expansion of macro ‘VAL’
#elif VAL==v2
^~~
preproc.c:17:2: warning: #warning "VAL not 1 or 2" [-Wcpp]
#warning "VAL not 1 or 2"
^~~~~~~
<builtin>: recipe for target 'preproc' failed
make: *** [preproc] Error 1
Однако, если я удаляю (double) в определении v1 и v2, он компилируется и запускается, как и ожидалось.
Обратите внимание, что в качестве альтернативного решения я сделал
#define USE_UART1
//#define USE_UART2
#if defined(USE_UART1)
# define UART UART1
# define PIN_TX 9
#elif defined(USE_UART2)
# define UART UART2
# define PIN_TX 2
#else
# warning "UART not correctly defined"
#endif
Но это включает в себя другую переменную [править: технически макрос, но практически другую группу символов, которые я должен отслеживать].
Мне было любопытно узнать причину этой ошибки компиляции и / или как ее исправить, если это возможно.
2 ответа
Квотирование C11
Глава §6.10.1p4
Перед оценкой, вызовы макросов в списке токенов предварительной обработки, которые станут выражением управляющей константы, заменяются (за исключением тех имен макросов, измененных определенным унарным оператором), как в обычном тексте. Если определенный токен генерируется в результате этого процесса замены, или использование определенного унарного оператора не соответствует одной из двух указанных форм до замены макроса, поведение не определено. После выполнения всех замен из-за расширения макроса и определенного унарного оператора все оставшиеся идентификаторы (включая лексически идентичные ключевым словам) заменяются на число pp 0, а затем каждый токен предварительной обработки преобразуется в токен....
В вашем коде вы сравниваете -
#if ((double)1)==((double)1)
поскольку double
не является действительным токеном, его заменяют на (0)
,
По сути вы сравниваете -
#if ((0)1)==((0)1)
Что не является допустимым константным выражением из-за синтаксических ошибок.
Когда я запускаю это с моим компилятором clang
, Я получил
ошибка: токен не является допустимым двоичным оператором в подвыражении препроцессора
Решение, которое вы упомянули, кажется хорошим. Вы не должны беспокоиться о "но это касается другой переменной", потому что это не переменные, а макросы. Макросы являются объектами времени компиляции и никоим образом не обременяют ваше время выполнения (память, давление регистра или даже время выполнения).
Нечто подобное вашей рабочей альтернативе является обычным способом решения таких проблем, как эта. Другой - иметь какую-то программу конфигурации, которая записывает соответствующие определения макросов в заголовочный файл.
Но это включает в себя другую переменную [...]
Нет, переменные вообще не задействованы. Макросы препроцессора не являются переменными, они являются макросами. Они не ведут себя как переменные, за исключением небольшого количества поверхностных способов. Предположим, что в противном случае вы пошли по неверному пути с самого начала. Макросы представляют собой фрагменты исходного кода, а переменные представляют места хранения в работающей программе.