22

What is the coolest somewhat practical metaprogramming hack you've done or seen done in the D programming language? Somewhat practical means excluding, for example, the compile-time raytracer.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
dsimcha
  • 67,514
  • 53
  • 213
  • 334

12 Answers12

9

An arbitrary precision type It generates ASM code at compile time (before the compiler does)

BCS
  • 75,627
  • 68
  • 187
  • 294
8

DParse in Scrapple tools is a templated parser generator. However, ldc is the only D compiler with a functioning compile time GC (but even then it has a couple oddly random crashes). Ive played with it a little and you can do some interesting things like config file parsing and stuff, but until a compile time GC is fully running you cannot do big things.

bmeck
  • 426
  • 2
  • 4
  • 5
    feeding it a D sized grammar (~200 productions) "only" takes 7 min and 700MB to compile. (full disclosure, I wrote dparse) – BCS Nov 04 '08 at 18:42
8

In terms of the outright coolest, I'd have to say Kirk McDonald's PyD (and other similar bindings) as these have do to a huge amount of work in detecting and handling lots of different types, as well as complex code generation.

That said, PyD only wins because BLADE technically uses CTFE, not templates.

On a more personal note, D templates have gotten extensive use in a research project of mine. It's a simulation framework where modules can define their own private data types. Exposing a new user type to the framework requires a single line of code which creates an XML parser for the type as well as associated network serialisation/deserialisation code.

DK.
  • 55,277
  • 5
  • 189
  • 162
7

The D/Objective-C Bridge uses templates to let you manipulate Cocoa objects in D.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
7

A united type template struct (It wont allow you to make unit errors.)

BCS
  • 75,627
  • 68
  • 187
  • 294
6

I'll answer my own question because this one didn't exist when I asked it. I wrote a patch to the garbage collector that uses templates and compile time introspection to generate the pointer offset information for arbitrarily complex user defined types to allow for precise heap scanning, instead of having this be done within the compiler.

dsimcha
  • 67,514
  • 53
  • 213
  • 334
6

My favorites would be ElemType and KeyType from tools.base:

template ElemType(T) {
  alias typeof((function() {
    foreach (elem; Init!(T)) return elem; assert(false);
  })()) ElemType;
}

template KeyType(T) {
  alias typeof((function() {
    foreach (key, elem; Init!(T)) return key; assert(false);
  })()) KeyType;
}
FeepingCreature
  • 3,648
  • 2
  • 26
  • 25
6

Compile time string hashing. You can use this to obfuscate embedded strings in your code. Just search for "hash". quite a few other interesting samples on that page, too.

philsquared
  • 22,403
  • 12
  • 69
  • 98
6

One example is the bitfields facility in D's standard library which generates code for bit field manipulation starting from a user-specified layout.

The Tuple facility is another example. It generates a tuple based on user-provided types and optional names. There isn't a lot of generative umph in there save for injecting the named fields, but I think it's an illustrative example.

Without knowing of Lambert's exploit I just added memoize to the standard library - see here for the documentation, here for the code, and here for the related newsgroup discussion.

Another facility I worked on is a higher-order function that tabulates an integral or real-valued function (e.g. offers fast exponential). That's not ready for release yet.

Once object creation will be allowed during compilation, it will be easy to create e.g. regular expression engines that do all automata generation during compilation.

Quonux
  • 2,975
  • 1
  • 24
  • 32
Andrei Alexandrescu
  • 3,214
  • 19
  • 17
4

I wrote a memoize() function whose header is this (the code is a bit long for pasting here):

auto memoize(TFunc)(TFunc func);

What it does is, you give it a function's address, and it returns a strongly-typed delegate (same signature and return type as the original function) that caches the return values of the original function, so that calling it twice with the same parameter only calls the underlying function once. For instance, here is a memoized, "recursive" definition of the Fibonacci sequence that executes in linear, rather than exponential, time:

uint fib(uint n) { return n > 0 ? n > 1 ? memoize(&fib)(n - 1) + memoize(&fib)(n - 2) : 1 : 0; }

You can call it normally, as in: fib(1000);


Edit: The previous code whose link I posted was rather hideous; this version is much more elegant.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 1
    Post the code to github and share it. Well, if you want to. – he_the_great Dec 14 '10 at 16:41
  • 2
    Thank you for the suggestion! Yes, I do want to share it. :) I'd never heard of site, but I signed up just now. I'm a little confused as to how it works, though... do I need to create a key pair and everything? Or am I on an unrelated part of the site? – user541686 Dec 14 '10 at 20:00
  • 1
    If you want to do any work on your own repo you will need a key pair (I do suggest it). But you can also create a gist easily: https://gist.github.com/ – he_the_great Dec 15 '10 at 04:00
  • 2
    Cool! Here's the code, if anyone's interested: https://gist.github.com/741782#file_memoize.d – user541686 Dec 15 '10 at 08:43
  • @Mehrdad: Do you still have the code? The gist is broken. – Stefan Majewsky Oct 13 '12 at 09:37
  • 1
    @StefanMajewsky: Ah yes I do, sorry about that. `auto memoize(F)(F f) if (isCallable!(f)) { alias ParameterTypeTuple!(F) P; return delegate(P args) { static typeof(f(args))[Tuple!(F, P)] cache; auto key = tuple(f, args); return key in cache ? cache[key] : (cache[key] = f(args)); }; }` – user541686 Oct 13 '12 at 09:43
2

Mixins to read and write simple structures from a Stream object:

template TStructReader() {
        private alias typeof(*this) T;
        static T opCall(Stream stream) {
                assert(stream.readable);
                T ret; stream.readExact(&ret, T.sizeof);
                return ret;
        }
}

template TStructWriter() {
        private alias typeof(*this) T;
        void write(Stream stream) {
                assert(stream.writeable);
                stream.writeExact(this, T.sizeof);
        }
}

Use it like this:

align (1) struct MyStruct {
        ... definitions here ...
        mixin TStructReader;
        mixin TStructWriter;
}

auto ms = MyStruct(stream);
ms.write(stream);
A. Fournier
  • 152
  • 5
2

LuaD also extensively uses metaprogramming to seamlessly interact with Lua. You can register a whole class with a single command.

Trass3r
  • 5,858
  • 2
  • 30
  • 45