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, Я получил

ошибка: токен не является допустимым двоичным оператором в подвыражении препроцессора

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

Нечто подобное вашей рабочей альтернативе является обычным способом решения таких проблем, как эта. Другой - иметь какую-то программу конфигурации, которая записывает соответствующие определения макросов в заголовочный файл.

Но это включает в себя другую переменную [...]

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

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