Day 40.
Представьте, что вы хотите на препроцессоре писать не макросами, а на каком-нибудь Lisp-like языке программирования (ну просто попытайтесь представить, что вы этого хотите).
Есть такая библиотека
ORDER_PP:
ORDER_PP(8to_lit(8add(8, 5))) // 13
или
ORDER_PP(
8to_lit(
8mul(
8add(2,3),
8add(4,5)
)
)
) // 45
Язык на самом деле мощнее. Можно объявлять переменные, писать функции, рекурсию, списками пользоваться, ленивые вычисления мутить (вроде как).
Можем вот факториал вычислить (это всё будет вычислено препроцессором!):
ORDER_PP
(
(8define FACT
(8fn (8N)
(8if (8zero 8N)
1
(8mul 8N
(FACT (8sub 8N 1)))))
))
ORDER_PP(8to_lit (FACT 6))
(возможно оно не скомпилируется, но суть такая)
Как это работает? И что это за 8 перед командами?
Я не буду рассказывать про устройство библиотеки, но расскажу основной механизм.
Во время лексического анализа препроцессор не знает ни про какие числа. Он знает про токены языка. А они бывают разные:
identifier
pp-number
string-literal
punctuator
... and others
Нас интересует pp-number. Ниже примеры того, что является pp-number:
123
1e10
0xFF
3.14
1E+10
0x123ABC
1abc
123hello
8X
8let
8mul
0foobar
И пусть вторую пачку компилятор не сможет разобрать как литералы, это всё равно pp-number (данные, а не identifier). А макросы ищутся только среди идентификаторов.
Поэтому библиотека пользуется такими pp-number сколько нужно и не боится, что препроцессор раскроет конструкции раньше времени (8 для префикса автор выбрал по личной симпатии видимо).
ORDER_PP пользуется этой возможностью хранить программу как данные и где-то внутри библиотеки в нужный момент происходит склейка pp-numbers в полноценные макросы:
ORDER_PP_8mul // for example
После этого перехода в мир стандартных макросов уже начинается жоское вычисление всего вокруг.
Чего только не напридумывают.
@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.