One of D's powers is its compile-time function evaluation (CTFE) system. Combined with introspection, generic programs can be written and heavy optimizations can be achieved.
Traits allow the accepted input to be explicitly specified.
For example splitIntoWords
can operate on any arbitrary string type:
S[] splitIntoWord(S)(S input)
if (isSomeString!S)
This applies to template parameters as well and myWrapper
can ensure that the
passed-in symbol is a callable function:
void myWrapper(alias f)
if (isCallable!f)
As a simple example, commonPrefix
from std.algorithm.searching
, which returns the common prefix of two ranges,
will be analyzed:
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)
This means that the function is only callable and thus compiles if:
r1
is save-able (guaranteed by isForwardRange
)
r2
is iterable (guaranteed by isInputRange
)
pred
is callable with element types of r1
and r2
r1
isn't a narrow string (char[]
, string
, wchar
or wstring
) - for simplicity, otherwise decoding might be needed
Many APIs aim to be general-purpose, however we want to avoid any extra runtime cost for the convenience of this generalization. With the power of introspection and CTFE, it is possible to specialize a method at compile-time to achieve the best performance given the input types.
A common problem is that in contrast to arrays you might not know the exact length
of a stream or list before walking through it.
Hence a simple implementation of the std.range
method walkLength
which can be substituted for any iterable type would be:
static if (hasMember!(r, "length"))
return r.length; // O(1)
else
return r.walkLength; // O(n)
commonPrefix
The use of compile-time introspection is ubiquitous in Phobos. For example
commonPrefix
differentiates between RandomAccessRange
s
and linear iterable ranges because in RandomAccessRange
it's possible to jump
between positions and thus speed-up the algorithm.
std.traits wraps most of
D's traits except for some like
compiles
that can't be wrapped as it would lead to an immediate compile error:
__traits(compiles, obvious error - $%42); // false
Additionally for debugging purposes D provides a couple of special keywords:
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);
}
With D's CLI evaluation one doesn't even need time
- CTFE can be used!
rdmd --force --eval='pragma(msg, __TIMESTAMP__);'