Metaprogramming in D : Some Real-world Examples

rmcguire rjmcguire at gmail.com
Fri Nov 13 04:30:15 PST 2009


Bill Baxter <wbaxter at gmail.com> wrote:
 
> On Mon, Nov 9, 2009 at 4:09 PM, Walter Bright
> <newshound1 at digitalmars.com> wrote:
>> Looks like Bill Baxter is giving a presentation on D Nov. 18!
>>
>> http://www.nwcpp.org/
> 
> Yep, that's right, and I'd be quite grateful to you smart folks here
> if you could share your meta-programming favorites with me!   If
> you've got a real-world example of meta-programming in D that you
> think is particularly handy, then please send it my way
> 
> I'm looking for small-but-useful things that are easy to explain, and
> make something easier than it would be otherwise.  Things like places
> where static if can save your butt,  or loop unrolling,  and passing
> code snippets to functions like in std.algorithm.
> 
> Things like a compile-time raytracer or regexp parser (though quite
> cool!) are not what I'm after.  Too involved for a short talk.
> 
> --bb
> 

I've use the code below to generate wrappers for my hessian serialization 
implementation.

its not all absolutely necessary but is only 130 lines.

import std.stdio;
import std.typetuple;
import std.traits;
import std.metastrings;

string[] splitFuncs(string str) {
    string[] res;
    while (str.length > 0) {
        while (str.length > 0 && (' ' == str[0] || ',' == str[0])) {
            str = str[1..$];
        }
        int to = 0;
        for (; to < str.length && str[to] != ' ' && str[to] != ','; ++to) {}
        if (to > 0) {
            res ~= str[0..to];
            str = str[to..$];
        }
    }
    return res;
}

string MethodTypeTuple_mixin(alias a)(string[] methods) {
	string ret = "TypeTuple!("~ "typeof(&C.init."~methods[0]~")";
	foreach (method; methods[1..$]) {
		ret ~= ",typeof(&C.init."~method~")";
	}
	ret ~= ")";
	return ret;
}



// test case

class A {
	int a;
	this(int a) {
		this.a = a;
	}
	int getInt(string intname) {
		return a;
	}
	
	void setInt(int i) {
		a = i;
	}
	string getString(string s) {
		return s ~"1234";
	}
}


string ProxyMethods_mixin(alias C, string methodstr)() {
	string ret;
	foreach(i, t; mixin(MethodTypeTuple_mixin!(C)(splitFuncs(methodstr)))) {
		// output function header
		ret ~= "\t"~ReturnType!(t).stringof ~" "~ splitFuncs(methodstr)[i]~"(";
		// output first arg
		ret ~= ParameterTypeTuple!(t)[0].stringof~" arg";
		// output remainder of args
		foreach (j, t1; ParameterTypeTuple!(t)[1..$]) { 
			ret ~= ","~t1.stringof~" arg"~std.metastrings.ToString!(j);
		}
		// output body
		ret ~= ") {\n";
		// output serialization code
		// send method name
		ret ~= "\t\twritefln(\"serialize docall id\"); // the method call byte 
id\n";
		ret ~= "\t\t//serialize!(string)(\""~splitFuncs(methodstr)[i]~"\"); /+ 
the method name +/\n";
		// send args
		ret ~= "\t\t//serialize!("~ ParameterTypeTuple!(t)[0].stringof~")
(arg); /+ the first argument +/\n";
		foreach (j, t1; ParameterTypeTuple!(t)[1..$]) {
			ret ~= "\t\t//serialize!("~ t1.stringof ~")(arg"~ToString!(j)~"); /
+ argument "~ToString!(j)~" +/\n";
		}
		// receive return type
		static if (!is(ReturnType!(t) == void)) {
			pragma(msg, "WARNING: this will always result in Range violation 
due to no real data. THIS IS JUST A DEMO");
			pragma(msg, "\t\t need to implement the actual send/receive");
			ret ~= "\t\t//return deserialize!("~ ReturnType!(t).stringof ~")
(buffer);\n";
			// this is just here to make it still compile even though I've 
commented out the real deserialize return above
			static if (is(ReturnType!(t) == int)) {
				ret ~= "\t\treturn 0;\n";
			} else {
				ret ~= "\t\treturn \"\";\n";
			}
		}
		ret ~= "\t}\n";
	}
	return ret;
}


string ProxyClass_mixin(alias C, string methodstr)() {
	string ret = "new class { ubyte[] buffer;\n";
	ret ~= ProxyMethods_mixin!(C, methodstr)();
	ret ~= "}\n";
	pragma(msg, MethodTypeTuple_mixin!(C)(splitFuncs(methodstr)));
	
	return ret;
}

class ProxyClass(alias C, string methodstr) {
		ubyte[] buffer;
		this() { }
		mixin(ProxyMethods_mixin!(C,methodstr)());
}

/+ void serialize(T)(T value) {
	writefln("serialize");
}
T deserialize(T)() {
	writefln("deserialize");
	static if (is(T==int)) {
		return 1;
	} else
	return "asdf";
} +/

void main() {
	pragma(msg, ProxyClass_mixin!(A,cast(string)"getInt setInt")());
	writefln(ProxyClass_mixin!(A,cast(string)"getInt setInt")());
	
 	auto c = mixin(ProxyClass_mixin!(A,cast(string)"getInt setInt getString")
());
	//pragma(msg, typeof(c).stringof);
	
	writefln("c.getInt(\"adsf\"): "~ c.getString("asdf"));
	
	auto pc = new ProxyClass!(A, cast(string)"getInt setInt getString");
	writefln("ProxyClass: "~ pc.getString("asdf"));
}




More information about the Digitalmars-d-announce mailing list