views:

288

answers:

4

I hear that the D language has powerful metaprogramming features for executing functions at compile time. That sounds very exciting, but I find it difficult to think of practical examples of things that are hard to accomplish without them.

Can anyone give some examples of situations where D's metaprogramming features comes in very handy?

+8  A: 

If you want practical examples of how to use D's metaprogramming facilities (CTFE, or compile time function evaluation, is just one of these, and not even the most important one) look no further than Phobos, the D2 standard library. Much of this code is written by Andrei Alexandrescu, who invented a lot of the template metaprogramming techniques in C++, and is now working with Walter Bright on the design and implementation of D.

The best modules to look in are std.range and std.algorithm. These are almost entirely composed of templates, where designed by Andrei, and are surprisingly readable given the amount of metaprogramming they use. I've contributed significantly to both of these modules and reading the code that was there when I started was basically how I learned.

All of the code is licensed under the (extremely permissive) Boost license and can be viewed directly from your browser at the Phobos Trac site on dsource.org.

Edit: To give you a roadmap of what you're looking at, D's metaprogramming facilities basically fall into 4 categories:

  1. Templates, which are basically like C++ templates, but with some added features like static if',static assert`, variadic templates, and constraints, which are basically like concepts but simpler.

  2. Compile time reflection/introspection. This includes the builtin is() expressions and __traits, as well as the standard library module std.traits.

  3. Mixins. These allow you to take either a template (template mixins) or a compile time string (string mixins) and evaluate it as code in the current scope. String mixins can be thought of as being kind of like an eval statement, except that the string is evaluated as code at compile time instead of at runtime.

  4. Compile time function evaluation, or CTFE, which allows functions that meet certain criteria to be evaluated at compile time. One important use of CTFE is that, combined with string mixins, you can generate code as a string at compile time, and then evaluate it as code in the scope where the mixin statement occurs. For examples of this, see std.range.Lockstep and std.range.OutputRangeObject, which I recently checked into the SVN releases of Phobos.

dsimcha
+7  A: 

One really cool and practical usage of compile time function execution is for generating code at compile time, possibly from config files, or maybe scripts.

Here's a simple example of processing a file at compile time.

main.d

string make_ints(string s)
{
    string ret = "";
    foreach (varname; split(s))
        ret ~= "int " ~ varname ~ "; ";
    return ret;
}

void main()
{
    mixin(make_ints(import("script")));
    foo = 1;
    bar = 2;
    xyz = 3;
}

script

foo bar xyz

At compile time, the file "script" will be read, split at spaces, and then make_ints returns int foo; int bar; int xyz; directly into the D code, ready for those variables to be used.

While this is a useless example, you could easily see how this could be used to read values from a config file (maybe values for the size of a cache, or something like that). Games could make use of this to generate raw D code from scripts, which will be great for performance (typically games resort to using interpreted code for scripting, and suffer performance wise).

You could also use this for automatic performance tuning. Say you have some constant X, which can be tweaked to affect performance in various ways, but you don't know what value of X will give you the best performance. You could put X in a file, read it in at compile time for use, try some other values at run time and put the best one back into the file. That way, you get gradually improving performance without having to do anything manually.

Peter Alexander
+3  A: 

I'm not well versed in the purposes of meta-programming. Here is my view on the three main constructs found in D.

  • Compile Time Function Evaluation is about performance. Making the the compiler do as much work as possible so that less is done once the program is actually run.
    • Like building a cache of prime numbers, if you program makes use of them often.
  • Templates are about eliminating algorithm duplication. It isn't just generic programming, but since D has CTFE it isn't needed in the same cases as C++.
  • Mixins are about being able to generate code at compile time that is added to the program. It relies on things like templates and CTFE, but still an important piece not provided by the others.
he_the_great
Part of the value of CTFE is for generating code to use with mixins. For example, look at std.range.Lockstep or std.range.OutputRangeObject in the SVN versions of Phobos. (I wrote both of these.)
dsimcha
+3  A: 

The most practically interesting use of it I know of is a library I wrote* for using the type checker to enforce unit safety (that is forbidding adding distances to times and providing the correct conversion when adding meters to feet). The same can and has been done in C++ but doing it in D at compile time is little harder than it would be to do it at run time.

* sorry about the shameless plug.

BCS