Залог мощи D - его система исполнения функций во время компиляции (CTFE). Благодаря интроспекции можно писать обобщённые программы и добиваться серьёзной оптимизации.
Типажи помогают явно указать, какой ввод является приемлемым.
К примеру, функция splitIntoWords
может работать с любым строковым
типом:
S[] splitIntoWord(S)(S input)
if (isSomeString!S)
Это применимо и к параметрам шаблонов. myWrapper
проверяет, что
переданный символ можно вызывать (как функцию):
void myWrapper(alias f)
if (isCallable!f)
Проанализируем простой пример - commonPrefix
из std.algorithm.searching
, возвращающий общий префикс двух диапазонов:
auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2)
if (isForwardRange!R1
isInputRange!R2 &&
is(typeof(binaryFun!pred(r1.front, r2.front)))) &&
!isNarrowString!R1)
Вызов такой функции скомпилируется только если:
r1
можно сохранить (гарантируется isForwardRange
)
r2
можно итерировать (гарантируется isInputRange
)
pred
можно вызвать с параметрами типов элементов r1
and r2
r1
- не "узкая" строка (char[]
, string
, wchar
или wstring
) - для простоты, иначе может потребоваться декодирование
Многие API стремятся расширить область их применения, однако при этом не хотят расплачиваться производительностью за такую обобщённость.
При помощи мощных механизмов интроспекции и CTFE становится возможным специализировать метод во время компиляции для достижения наилучшей производительности для конкретных типов.
Часто встречающаяся проблема - неизвестная заранее длина потока или списка.
В std.range
определён простой метод walkLength
, обобщённый для
любого итерируемого типа:
static if (hasMember!(r, "length"))
return r.length; // O(1)
else
return r.walkLength; // O(n)
commonPrefix
Интроспекция во время компиляции применяется в Phobos повсеместно.
К примеру, commonPrefix
различает диапазоны со случайным доступом
(RandomAccessRange
) и линейно-итерируемые диапазоны, поскольку
при наличии случайного доступа можно свободно перепрыгивать с места
на место, за счёт чего ускорить алгоритм.
std.traits - оболочка вокруг
большинства типажей D, за исключением
некоторых, например compiles
, который невозможно "обернуть":
__traits(compiles, obvious error - $%42); // false
В отладочных целях D предоставляет несколько специальных ключевых слов:
void test(string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__,
string func = __FUNCTION__, string pretty = __PRETTY_FUNCTION__)
{
writefln("file: '%s', line: '%s', module: '%s',\nfunction: '%s', pretty function: '%s'",
file, line, mod, func, pretty);
}
А благодаря вычислениям из командной строки, можно забыть про time
-
будет использовано CTFE!
rdmd --force --eval='pragma(msg, __TIMESTAMP__);'