Reading — step 1 of 4
Learn
D's metaprogramming is more accessible than C++ templates. Three forms: template mixins, string mixins, and CTFE.
Template mixins
Inject a template's contents into the current scope:
mixin template Logging() {
void log(string msg) {
writeln("[", typeof(this).stringof, "] ", msg);
}
}
class Service {
mixin Logging; // injects log() method
}
auto s = new Service;
s.log("started"); // "[Service] started"
Useful for cross-cutting concerns: logging, equals/hash, accessors.
String mixins
Inject literal source code generated at compile time:
int x = mixin("3 + 4"); // x = 7
// Generated accessor:
string genAccessor(string name, string type) {
return type ~ " _" ~ name ~ ";\n" ~
type ~ " " ~ name ~ "() { return _" ~ name ~ "; }\n" ~
"void " ~ name ~ "(" ~ type ~ " v) { _" ~ name ~ " = v; }";
}
struct Person {
mixin(genAccessor("name", "string"));
mixin(genAccessor("age", "int"));
}
CTFE generates the source string; mixin compiles it. Powerful but harder to read.
CTFE — Compile-Time Function Execution
Any pure function with compile-time-known inputs runs at compile time:
long factorial(long n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
enum FACT_10 = factorial(10); // computed during compile
static immutable lookup = factorial(20); // also CTFE
__traits and reflection
D has compile-time reflection — query types and members:
import std.traits;
struct Person {
string name;
int age;
}
static foreach (member; __traits(allMembers, Person)) {
writeln(member); // "name", "age"
}
static if (hasMember!(Person, "name")) {
// ...
}
std.traits has dozens of helpers: isArray!T, isCallable!T, Parameters!fn, ReturnType!fn.
Auto-serializer example
Combine the above for runtime-free JSON serialization:
import std.format, std.traits;
string toJson(T)(T obj) {
string result = "{";
bool first = true;
static foreach (member; __traits(allMembers, T)) {
if (!first) result ~= ",";
result ~= format(`"%s":"%s"`, member, __traits(getMember, obj, member));
first = false;
}
return result ~ "}";
}
struct User { string name; int age; }
auto u = User("Ada", 36);
writeln(toJson(u)); // {"name":"Ada","age":"36"}
Similar to what frameworks do — except entirely at compile time.
When to reach for metaprogramming
- DSLs (regex compilers, parser generators)
- Cross-cutting concerns (logging, validation)
- Generic libraries (serializers, ORMs)
- Performance — moving work from runtime to compile time
D's metaprogramming is part of the language, not a separate macro system. That's a big quality-of-life improvement over C/C++.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…