Перейти к содержанию
    

Вызов «xcat(xcat(1, 2), 3)» будет заменён на «123»

Что в книге Кернигана с Ричи, что на просторах интернет говорится о том что во вложенных макросах надо объединять строки в два этапа:

#define cat(x,y) x##y

#define xcat(x,y) cat(x,y)

и тогда уже будет нормально выполняться

xcat(xcat(1,2),3)

Как пройти по шагам, что при этом будет выполняться?

В Википедии бред какой-то наблюдаю. Также и во многих интернетных статьях.

https://ru.wikipedia.org/wiki/%D0%9F%D1%80%...80_%D0%A1%D0%B8

http://we.easyelectronics.ru/Soft/preprocessor-c.html

https://www.opennet.ru/docs/RUS/cpp/cpp-5.html

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как пройти по шагам, что при этом будет выполняться?

В свойствах проекта IAR, например, есть чекбокс "Preprocessor output to file".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В свойствах проекта IAR, например, есть чекбокс "Preprocessor output to file".

Да ну... там конечный результат будет выдан. Мне промежуточные нужны, как в Википедии показано. Но там неправильно, на мой тупой взгляд.

 

P.S. и стандарт листал, и все равно не понял

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

То есть, сначала в аргументе макрофункции произошла подстановка и выполнение... Почему?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Точнее, вопрос, почему в макрофункции

cat(cat(1,2),3)

не складываются так

cat(12,3)

123

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Точнее, вопрос, почему в макрофункции

cat(cat(1,2),3)

не складываются так

cat(12,3)

123

Из вики:

Если в замещающей последовательности перед параметром не стоит знак #, если и ни перед ним, ни после него нет знака ##, то лексемы аргумента проверяются на наличие в них макровызовов; если таковые есть, то до подстановки аргумента в нём выполняется раскрытие соответствующих макросов.

В определении cat есть ##, поэтому в аргументах раскрытие макросов не производится, они вставляются на место формальных параметров как есть (в отличие от xcat, где ни #, ни ## нет).

Получается первый аргумент - cat(1,2) и к нему присоединяется второй - 3

 

После подстановки к получившемуся тексту снова пытаются применить имеющийся набор макроопределений, но исключив из него уже применявшиеся (для предотвращения бесконечной рекурсии).

Определение cat как раз исключено и поэтому в получившемся тексте cat(1,2) не будет раскрываться.

 

В итоге получится: cat(1,2)3

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Точнее, вопрос, почему в макрофункции

cat(cat(1,2),3)

не складываются так

cat(12,3)

123

Вот объяснение (отсюда):

 

3.10.6 Argument Prescan

 

Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens. After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded. The result is that the arguments are scanned twice to expand macro calls in them.

 

При раскрытии xcat(xcat(1, 2), 3) сначала, как полагается, производится прескан аргументов. Раскрывается первый аргумент:

xcat(1, 2) -> cat(1, 2) -> 12.

Прескан второго аргумента дает результат 3.

 

Теперь раскрывается сам макрос:

xcat(12, 3) -> cat(12, 3) -> 123.

 

Ключевая фраза "unless they are stringified".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ага, так в выражении:

Если в замещающей последовательности перед параметром не стоит знак #, если и ни перед ним, ни после него нет знака ##, то лексемы аргумента проверяются на наличие в них макровызовов; если таковые есть, то до подстановки аргумента в нём выполняется раскрытие соответствующих макросов.

"если таковые есть" относится к макровызовам, а не к знакам # и ##

 

Так вижу правило макроподстановок касательно стрингизации и склеивания:

Если в замещающем выражении макроопределения перед параметром нет знака # или между параметрами нет знака ##, то лексемы параметров проверяются на наличие в них

макроопределений и замещаются раскрытыми макроопределениями. В противном случае параметры не проверяются на наличие в них макроопределений, а подставляются в замещающую последовательность как есть.

 

Рассмотрим макроподстановки xcat(xcat(1,2),3)

Видим первый внешний xcat. Смотрим, в его макроопределении (cat(x,y)) нет # или ##. Проверяем параметры xcat(1,2) и 3 этого внешнего xcat на наличие макроопределений. Находим xcat внутренний. Смотрим, в его макроопределении (cat(x,y)) нет # или ##. Проверяем его параметры 1 и 2 внутреннего xcat на наличие макроопределений. Для них макроопределений нет. Подставляем макроопределение внутреннего cat, который превращается в 12. Подставляем вместо первого параметра во внешний cat. Подставляем макроопределение внешнего cat. Получаем 123.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если есть желание совсем "надломить моск" :smile3009: , можно почитать и по-разбираться по ссылкам:

C Pre-Processor Magic

C Preprocessor tricks, tips, and idioms

Я даже рискнул кое-что из этого применить в своих проектах...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, почитаем-с! Я, в принципе, пользуюсь макро, насколько мне нужно. И с X-macro разбирался, но не применял пока.

Вот этого нюанса с ## не знал.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно проще - # и ## применяются ДО ТОГО, как производятся раскрытия макроподстановок. И только в том, что получится ПОСЛЕ # и ## ищутся и подставляются макросы.

Собственно всё :)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем это тема для меня пока что не совсем понятна.

Пытаюсь сделать следующее.

Есть функция

GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_SPI1);

Здесь GPIO_AF_SPI1 - это тоже некий #define.

 

И я хочу сделать следующее. В файле настроек иметь что-то наподобие

#define LCD_SPI_MODULE SPI1

При этом ключевое слово SPI1 - это тоже макрос на начало области памяти модуля SPI1.

 

Хочу сделать так

#define GPIO_AF_EXPAND_E(a, b) a##b
#define GPIO_AF_EXPAND(a, b) GPIO_AF_EXPAND_E(a, b)
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_EXPAND(GPIO_AF_, LCD_SPI_MODULE));

Но один фиг не работает так как надо, не получаю я после разворачивания макроса GPIO_AF_SPI1 (пишет Error: L6218E: Undefined symbol GPIO_AF_ (referred from hardware.o).).

 

Это вообще реально? Логику макросов вообще тут понять сложно, с учетом того, что SPI1 и GPIO_AF_SPI1 - тоже макрос-объекты.

Изменено пользователем Arlleex

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это вообще реально? Логику макросов вообще тут понять сложно, с учетом того, что SPI1 и GPIO_AF_SPI1 - тоже макрос-объекты.

Вы не привели законченного куска кода со всеми вложенными макросами, поэтому не совсем понятно почему вылезла именно ошибка Undefined symbol GPIO_AF_

 

На мой взгляд, если где-то есть описание типа:

#define SPI1 0x1234

#define LCD_SPI_MODULE SPI1

то должна быть ошибка

Undefined symbol GPIO_AF_0x1234

 

потому что каждый раз макросы разворачиваются до конца своей вложенности.

И можно ли на конкретном проходе макроподстановки ограничить разворачивание вложенности одним уровнем, это вопрос интересный...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

потому что каждый раз макросы разворачиваются до конца своей вложенности.

Вот. Это и есть ключ к ответу на мой вопрос :laughing:

 

А насчет всех макросов, то вот:

#define PERIPH_BASE 0x40000000
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
#define SPI1_BASE (APB2PERIPH_BASE + 0x3000)
#define SPI1 ((SPI_TypeDef *)SPI1_BASE)

#define GPIO_AF_SPI1 ((uint8_t)0x05)

Видимо действительно он раскручивает все макросы, которые может развернуть, до самого конца. А потом пытается склеить, получается каша.

 

// HW_ADCBank1Init - функция инициализации периферии для работы с внешними АЦП.
// Параметры: нет.
// Возвращаемое значение: нет.
static void HW_ADCBank1Init(void)
{
  #define LCD_SPI_MODULE SPI1
  #define GPIO_AF_EXPAND_E(a, b) a##b
  #define GPIO_AF_EXPAND(a, b) GPIO_AF_EXPAND_E(a, b)

  GPIO_PinAFConfig(GPIO_ADC_SPI1_SCK.GPIO,  GPIO_ADC_SPI1_SCK.PinSource,  GPIO_AF_EXPAND(GPIO_AF_, LCD_SPI_MODULE));
  GPIO_PinAFConfig(GPIO_ADC_SPI1_MISO.GPIO, GPIO_ADC_SPI1_MISO.PinSource, GPIO_AF_SPI1); // GPIO_AF_SPI1 я пытаюсь получить и в предыдущей строке
  ...
  ...
  ...

выдает в консоли:

User\Hardware.c(449): warning:  #223-D: function "GPIO_AF_" declared implicitly

Стоит переписать как

#define #define LCD_SPI_MODULE _SPI1
...
GPIO_PinAFConfig(GPIO_ADC_SPI1_SCK.GPIO,  GPIO_ADC_SPI1_SCK.PinSource,  GPIO_AF_EXPAND(GPIO_AF, LCD_SPI_MODULE));

то все работает и преобразуется во что нужно.

Однако жутко неудобен этот "хвост" в виде нижнего подчеркивания... Ведь в коде хотелось бы иметь

SPI_Cmd(LCD_SPI_MODULE, ENABLE);

а не

SPI_Cmd(SPI1, ENABLE);

Есть конечно решение "в лоб", это сделать несколько определений:

#define LCD_SPI_MODULE SPI1
#define _LCD_SPI_MODULE_ _SPI1

и использовать первый, где требуется именно имя SPI1, а второй - там где этот кусок текста слепляется с другими макросами, например, в имени GPIO_AF_SPI1.

 

P.S. Уважаемые, а можно ли в тегах [код] раскрашивать текст в разный цвет? А то хотелось бы акцентировать внимание на что-то, да не получается.

Изменено пользователем Arlleex

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...