D has no
preprocessor, but it has a powerful template system. Combined with
pragma(msg)
, it is a powerful machine for generating output at
compile time.
When trying these out, you should instruct the compiler to do just the compiling and not the linking. In DMD, the -c command-line option does this. Otherwise, you'll probably get a crash of some kind, especially with the last example.
Just as I've done with the C page, I'm going to begin with a "program" to print the "99 bottles of beer" lyrics.
template bottlesOfBeer(uint n) {
pragma(msg, toStr!(n) ~ bottleSP!(n) ~ " on the wall");
pragma(msg, toStr!(n) ~ bottleSP!(n));
pragma(msg, "Take one down, pass it around");
pragma(msg, toStr!(n-1) ~ bottleSP!(n-1) ~ " on the wall");
pragma(msg, "");
const int bottlesOfBeer = bottlesOfBeer!(n-1);
}
template bottlesOfBeer(uint n : 0) {
const int bottlesOfBeer = 0;
}
template bottleSP(uint n) {
const char[] bottleSP = n == 1 ? " bottle of beer" : " bottles of beer";
}
template toStr(uint n) {
const char[] toStr = "123456789"[n < 10 ? 0 : n/10 - 1 .. n/10]
~ "0123456789"[n%10 .. n%10 + 1];
}
const int dummy = bottlesOfBeer!(99);
const uint rows = 16;
const uint width = 5;
// Number formatting
template toStr(uint w, uint n) {
const char[] toStr = toStr!(w-1, n/10) ~ "0123456789"[n%10 .. n%10 + 1];
}
template toStr(uint w, uint n : 0) {
const char[] toStr = spaces!(w);
}
template spaces(uint w) {
const char[] spaces = " " ~ spaces!(w-1);
}
template spaces(uint w : 0) {
const char[] spaces = "";
}
// Calculating cells of Pascal's triangle
template pascal(uint row, uint col : 0) {
const uint pascal = 1;
}
template pascal(uint row, uint col : row) {
const uint pascal = 1;
}
template pascal(uint row, uint col) {
const uint pascal = pascal!(row-1, col) + pascal!(row-1, col-1);
}
// Presenting the results in rows
template pascalRow(uint row) {
const char[] pascalRow = spaces!(width * (rows - row - 1) / 2) ~
pascalRow!(row, 0u);
}
template pascalRow(uint row, uint fromCol) {
const char[] pascalRow = toStr!(width, pascal!(row, fromCol))
~ pascalRow!(row, fromCol + 1);
}
template pascalRow(uint row, uint fromCol : row) {
const char[] pascalRow = toStr!(width, 1);
}
// Doing the whole thing
template pascalTriangle(uint fromRow) {
pragma(msg, pascalRow!(fromRow));
const uint pascalTriangle = pascalTriangle!(fromRow + 1);
}
template pascalTriangle(uint fromRow : rows) {
const uint pascalTriangle = 0;
}
uint dummy = pascalTriangle!(0);
They get more involved than this. With the help of D's built-in complex number type, one can write a CTP that visualises the Mandelbrot set. Of course, the resolution is nothing compared to what you can get out of Fractint, but it works.
const real bailout = 4;
const uint iter = 20;
template image(real xMin, real yMin, real xStep, real yStep,
uint xCount, uint yCount) {
static if (yCount == 0) {
const int image = 0;
const real xZoom1 = real.nan, yZoom1 = real.nan;
const real xZoom2 = real.nan, yZoom2 = real.nan;
} else {
static if (yMin <= 0) {
/* take the absolute value of yMin in order to use already
* calculated data if available
*/
alias row!(-yMin, xMin, xStep, xCount) thisRow;
const real yNext = -yMin - yStep, yStepNext = -yStep;
} else {
alias row!(yMin, xMin, xStep, xCount) thisRow;
const real yNext = yMin + yStep, yStepNext = yStep;
}
pragma(msg, thisRow.row);
alias image!(xMin, yNext, xStep, yStepNext, xCount, yCount - 1) next;
static if (thisRow.xZoom == thisRow.xZoom) {
const real xZoom1 = thisRow.xZoom, yZoom1 = yMin;
const real xZoom2 = next.xZoom1, yZoom2 = next.yZoom1;
} else {
const real xZoom1 = next.xZoom1, yZoom1 = next.yZoom1;
const real xZoom2 = next.xZoom2, yZoom2 = next.yZoom2;
}
}
}
template point(real x, real y) {
const char point = point!(x + y*1i, x + y*1i, iter);
}
template point(creal pt, creal cur, uint itLeft) {
const char point = cur.re * cur.re + cur.im * cur.im < bailout
? point!(pt,
/* perform eight iterations at a time to improve speed and reduce
* the number of template instances generated
*/
(((cur * cur + pt) * (cur * cur + pt) + pt)
* ((cur * cur + pt) * (cur * cur + pt) + pt) + pt)
* (((cur * cur + pt) * (cur * cur + pt) + pt)
* ((cur * cur + pt) * (cur * cur + pt) + pt) + pt) + pt,
itLeft - 1) : '#';
}
template point(creal pt, creal cur, uint itLeft : 0) {
const char point = '.';
}
template row(real y, real xMin, real xStep, uint xCount) {
static if (xCount == 0) {
const char[] row = "";
const real xZoom = real.nan;
} else {
const char pt = point!(xMin, y);
const real xZoom = pt == '.' ? xMin
: row!(y, xMin + xStep, xStep, xCount - 1).xZoom;
const char[] row = point!(xMin, y)
~ row!(y, xMin + xStep, xStep, xCount - 1).row;
}
}
alias image!(-2, 1.25, 0.125, -0.125, 23, 21) image1;
pragma(msg, "");
alias image!(image1.xZoom2 - 0.125, image1.yZoom2 + 0.125,
0.015625, -0.015625, 17, 17) image2;
pragma(msg, "");
alias image!(image2.xZoom2 - 0.015625, image2.yZoom2 + 0.015625,
1.0/512, -1.0/512, 17, 17) image3;