Menu

Traits (Характеристики)

Одною з переваг D є її система виконання функцій під час компіляції (CTFE). У поєднанні з самоаналізом можна написати узагальнені програми та досягнути потужних оптимізацій.

Явні контракти

Traits дозволяють явно вказати, що може бути подано на вхід. Наприклад, 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 та r2
  • r1 не "вузький" рядок (char[], string, wchar чи wstring) - для простоти, в іншому випадку, може бути необхідне декодування

Спеціалізація

Багато API націлені на розширення області застосування, проте вони не хочуть платити швидкодією за таке узагальнення. Із силою самоаналізу та CTFE, можна сфокусуватися на методах часу компіляції для досягнення максимальної продуктивності, враховуючи певні вхідні типи.

Найпоширеніша проблема, що на відміну від масивів, ми повинні знати точну довжину потоку даних або списку перш, ніж працювати з ним. Тому ми маємо простий метод walkLength з std.range, який є узагальненим для будь-якого ітерабельного типу:

static if (hasMember!(r, "length"))
    return r.length; // O(1)
else
    return r.walkLength; // O(n)

commonPrefix

У бібліотеці Phobos самоаналіз під час компіляції використовується всюди. Наприклад, сommonPrefix розрізняє діапазони з випадковим доступом (RandomAccessRange) та лінійні ітерабельні діапазони, тому що у RandomAccessRange можна "перестрибувати" між позиціями і, таким чином, прискорювати виконання алгоритму.

Більше магії CTFE

std.traits - оболонка навколо більшості характеристик (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("файл: '%s', рядок: '%s', модуль: '%s',\nфункція: '%s', гарна функція: '%s'",
             file, line, mod, func, pretty);
}

А завдяки обчислень з командного рядка, можна забути про time - буде використано CTFE!

rdmd --force --eval='pragma(msg, __TIMESTAMP__);'

Поглиблення

rdmd playground.d