Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

The 'meta' metaprogramming library

Introduction

A general-purpose, compile-time library providing compile-time equivalents for many functions from the D run-time library. All of the metafunctions are executed at compile-time; they may produce data, but do not generate any run-time code.

The goals of this project are:

  1. to demonstrate the power of D's metaprogramming facilities, and
  2. to produce a library which is sufficiently intuitive and general purpose to become a standard library.

Overview

The following modules are compile-time counterparts of the runtime versions in the standard library.

  • meta.conv -- Compile-time conversions between strings and numeric values.
  • meta.ctype -- Simple ASCII character classification functions.
  • meta.math -- Mathematical functions for real and complex numbers.
  • meta.string -- String-handling functions
  • meta.regex -- Compile-time regular expressions.

Other modules have no runtime counterpart:

  • meta.generatetable -- Create compile-time look-up tables.
  • meta.qualifiedname -- Convert a D symbol to a text string.

And some modules are workarounds for limitations of current D compilers.

  • meta.strhack -- workarounds involving strings
  • meta.hack.* -- more serious workarounds, used internally by meta.

Tutorial

The functions in the meta library are entirely executed at compile time. If you've seen C++ template metaprogramming and been scared off by the obscure syntax, don't worry -- D metaprogramming is far more intuitive. In most cases, using a metafunction looks and behaves exactly the same as the corresponding compile-time function, except that there is a ! before the function call.

import meta.math;

real x = pow!(3.0, 10);

produces exactly the same result as

import std.math;

real x = pow(3.0, 10);

But there's an important difference. The metafunction pow!() is executed at compile time, so that it compiles to simply:

real x = 59049.0;

Big deal. A highly optimising compiler might be able to do this for pow(), as well. Then again, it might not. We'll quickly come to examples where it is very unlikely that the compiler can generate efficient code. Because metafunctions are executed at compile-time, they can only operate on quantities that are known at compile time. This means that variables cannot be used.

Metafunctions can return values of any constant type. For example, itoa!() converts an integer to a string:

 import meta.conv;

 const int a = 525;
 const char [] str = "The value of a is " ~ itoa!(a);
 assert(str == "The value of a is 525");

We can use this metafunction to print the value of constants at compile time. This can be extremely useful for debugging.

static if (x < MAXARRAYSIZE) {
 pragma(msg, " The value of x must be less than " ~ itoa!(MAXARRAYSIZE));
}

Floating point numbers can also be used. The fcvt!() metafunction converts a real to a string in %f format.

Here's an example which calculates and prints the value of PI at compile time, using Machin's formula:

import meta.math;  // for atan!()
import meta.conv;  // for fcvt!()

/// Machin's formula for pi
/// pi / 4 = 4 atan( 1 / 5 ) - atan( 1 / 239 ).

const MachinPI_4 = 4 * atan!( 1.0 / 5 ) - atan!( 1.0 / 239 );

pragma(msg, "PI = " ~ fcvt!(4.0 * MachinPI_4) );