Метапрограммирование 5 - сводим все воедино

Итак, в предыдущих статьях мы получили, что в общем случае шаблоны - это чистые квазицитаты; что любые аргументы шаблонов и макросов - это фрагменты AST (объекты типа expr). Поскольку в квазицитаты можно в общем случае подставлять любые фрагменты AST, а не только имена типов и числовые константы (как в С++), то совершенно логично, что шаблоны можно расширить и обобщить. Мы выяснили,  что практически аналогично шаблоны могут получать в качестве аргументов нечисловые константы и функции. Также было сделано предположение, что аргументами шаблонов могут быть и произвольные блоки кода. Кроме того, сами блоки кода могут быть параметризированы подобно шаблонам. Именно этим мы и займемся.



Если вспомнить Си, то там есть такая вещь как #define - элемент препроцессора, используемый для подстановок. Можно например, определить через #define некое сложное выражение
#define X (a+b*c-d/e&f << g | h )
и подставлять его в код. Разумеется, в Си подстановка велась на уровне исходного текста, что само по себе крайне нежелательно. Обычно дефайны использовались для целей
1. определение констант (т.е. данных)
2. простейшие inline-операции, такие например как максимум или минимум: min(x,y) ((x)<(y) ? (x):(y)). Круглые скобки вокруг переменных тут не случайны: вместо x и y могут быть не только числа или переменные, но и выражения (то есть как-бы фрагменты AST).
3. различные синтаксические обертки типа MESSAGE_MAP в MFC
4. макропеременные, используемые для условной компиляции
Конечно, встречаются и другие применения, но гораздо реже.

Сам по себе #define в Си ближе всего к понятию "параметризированный блок кода". Попытаемся реализовать аналог #define без препроцессора, на уровне работы с AST. Введем ключевое слово def для описания таких блоков (слово находится на одном уровне с описателями class, struct, fn, enum и т.д.). Тогда любые блоки кода можно будет описывать в стиле #define, но без недостатков, присущих препроцессору (по крайней мере, не нужно писать ужасные '\' в конце каждой строки).
def MyIF(x) { if(x>0) x++; }
Также можно определять данные (числа, строки),
C помощью def можно определять любые фрагменты AST для последующей вставки этих фрагментов в любые места программы.

Формально, оператор def, определенный таким образом, НИЧЕМ не отличается от шаблона (более того, и сишный #define, будучи использованным таким образом, дал бы похожий результат). Но в операторе def в качестве аргументных скобок используются круглые скобки. Возникает вопрос: возможно, допустимо использование круглых скобок и для других шаблонов (классов, функций)? Строго говоря, идеальным был бы вариант использования уникальных скобок для шаблонов и макросов (то есть конструкции #<>). В текущей версии языка выбрано именно это решение - все подстановки и шаблоны используют именно #<> в качестве аргументных скобок.
Вот пример того, что можно сделать на шаблонных макросах нового типа

def MyLOOP #<cond, body, maxcount>
{
   int i = 0;
   while(cond && i<maxcount)
   {
     body;
     i++;
   }
}

Использование в коде:
int x = 100, y = some_func();
MyLOOP#< (x>y), printf("Hello %d\n", x), 110 >;

Как видно, границы между шаблонами и макросами (в смысле С/С++)) действительно стираются. Если посмотреть внимательно, то в данном примере параметрами шаблона являются не только числа или типы, как в С++, а обычные выражения и блоки кода. Выражение "x<200" взято в скобки по понятным причинам - чтобы знак "больше" не был воспринят как закрывающая фигурная скобка. Что интересно, использование простых круглых или квадратных скобок полностью решило бы проблему и символ # не понадобился бы. Так что архитектурное решение о применении #<> неокончательное.

Таким образом, мы получаем следующую картину.
1. Шаблон - это квазицитата, аргументы шаблона - сплайс-параметры квазицитаты.
2. Шаблоном может быть любая конструкция языка, которая может декларироваться как самостоятельная сущность (т.е. не являющаяся частью "текущего потока кода"). Сюда относятся: классы, структуры, объединения, перечисления , функции, модули, свободные блоки кода.
3. Аргументом шаблона может быть любая конструкция языка вообще.
4. Все шаблоны и все аргументы шаблонов рассматриваются как фрагменты AST.

No comments:

Post a Comment