Traits

Một trong những sức mạnh của D là CTFE (compile-time function evaluation), hay khả năng tính toán các biểu thức lúc biên dịch chương trình. Cùng với introspection (nội soi), CTFE viết các chương trình ở dạng tổng quát và đạt được mức độ tối ưu cao .

Hợp đồng tường minh

Trait cho phép chỉ rõ những đầu vào được chấp nhận. Ví dụ, hàm splitIntoWords sau có thể chấp nhận bất kỳ kiểu chuỗi nào:

S[] splitIntoWord(S)(S input)
if (isSomeString!S)

Điều này cũng đúng với tham số mẫu, như ví dụ myWrapper sau đây có thể đảm bảo đầu vào là một hàm (có thể gọi được, isCallable)

void myWrapper(alias f)
if (isCallable!f)

Ví dụ, hàm commonPrefix từ thư viện std.algorithm.searching, là hàm dùng để trả về phần đầu chung giữa hai dải, bao gồm phép phân tích đầu vào như sau

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)

Trình biên dịch xác định việc gọi hàm này được không lúc biên dịch chương trình, và chỉ tiếp tục nếu các điều kiện sau thỏa mãn

  • r1 có thể lưu được (do isForwardRange)
  • r2 là một dải (do isInputRange)
  • pred gọi được với các kiểu phần tử của r1r2
  • r1 không phải là chuỗi hẹp (char[], string, wchar hay wstring) - cái này là để cho đơn giản, nếu không sẽ phải cần thêm các phép giải mã.

Đặc biệt hóa

Nhiều API hướng tới nhu cầu chung chung, nhưng chúng cũng không muốn phải trả giá phát sinh lúc chạy chương trình cho sự tổng quát hóa đó. Nhờ khả năng của nội soi (introspection) và CTFE, ta có thể đặc biệt hóa lúc biên dịch để đạt hiệu quả cao nhất ứng với một số kiểu đầu vào.

Một bài toán phổ biến là cần biết chính xác độ dài của danh sách hay stream trước khi duyệt qua (khác với khi xử lý mảng). Như ví dụ sau đây là một cách xét tới trường hợp đặc biệt để tránh dùng hàm tổng quát walkLength từ thư viện std.range:

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

commonPrefix

Phép nội soi lúc biên dịch được dùng khắp nơi trong Phobos. Ví dụ, trong định nghĩa của commonPrefix, có sự phân biệt giữa các dải truy cập ngẫu nhiên (RandomAccessRange) và dải truy cập tuyến tính, để tận dụng tốc độ cao khi có thể truy cập tùy ý các phần từ của dải truy cập ngẫu nhiên.

Thêm về phép lạ của CTFE

Thư viện std.traits tận dụng hầu hết các tính chất của trait trong D ngoại trừ vài chỗ như compiles thì không thể, bởi nó dẫn tới lỗi biên dịch

__traits(compiles, obvious error - $%42); // false

Từ khóa đặc biệt

Để hỗ trợ bổ sung cho mục đích gỡ lỗi, một số từ khóa được gán nghĩa đặc biệt, ví dụ hàm test sau cho thấy cách sử dụng chúng:

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);
}

Chế độ dòng lệnh của D thậm chí còn cho phép phân giải giá trị thời gian ngay lúc dịch chương trình:

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

Nâng cao

rdmd playground.d