On Fri, 14 Aug 2009 00:16:39 +1000, Daniel Keep wrote: > Bill Baxter wrote: >> On Thu, Aug 13, 2009 at 12:43 AM, Rory McGuire<rjmcgu...@gmail.com> >> wrote: >>> On Wed, 12 Aug 2009 17:03:17 -0700, Bill Baxter wrote: >>> >>>> On Wed, Aug 12, 2009 at 4:52 PM, Rory McGuire<rjmcgu...@gmail.com> >>>> wrote: >>>>> Here is some code I wrote which enables wrapping a proxy around an >>>>> object. I am using it for my serialization library. It works in >>>>> D1(1.046) and D2 (2.031) >>>>> >>>>> Posting it here for reference by all before I add to much of the >>>>> stuff specific to my use, should make it easier to follow. >>>>> >>>>> usage: new ProxyClass!(A, cast(string)"getInt setInt getString"); >>>>> would implement the methods getInt setInt and getString from A in >>>>> the new class. >>>>> >>>>> the code below will fail to compile but not before printing the >>>>> generated code to stdout. >>>>> >>>>> Shin Fujishiro has made some new templates for D2 which will make it >>>>> so I can get rid of the "setInt getInt getString" part which would >>>>> make the usage for D2: new ProxyClass!A; >>>>> which would be great! >>>>> >>>>> -Rory >>>>> >>>>> ============================================ // author: Rory >>>>> McGuire, rjmcgu...@gmail.com import std.stdio; import std.typetuple; >>>>> import std.traits; >>>>> import std.metastrings; >>>>> >>>>> //import serializer; >>>>> >>>>> // this CTF from somewhere on news.digitalmars.com 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\tbuffer ~= >>>>> serialize!(string)(\""~splitFuncs >>>>> (methodstr)[i]~"\", s_state); /+ the method name +/\n"; >>>>> // send args >>>>> ret ~= "\t\tbuffer ~= serialize!("~ >>>>> ParameterTypeTuple!(t) >>>>> [0].stringof~")(arg, s_state); /+ the first argument +/\n"; >>>>> foreach (j, t1; ParameterTypeTuple!(t)[1...$]) { >>>>> ret ~= "\t\tbuffer ~= serialize!("~ >>>>> t1.stringof >>>>> ~")(arg"~ToString!(j)~", s_state); /+ argument "~ToString!(j)~" >>>>> +/\n"; >>>>> } >>>>> // receive return type >>>>> static if (!is(ReturnType!(t) == void)) { >>>>> ret ~= "\t\treturn deserialize!("~ >>>>> ReturnType! >>>>> (t).stringof ~")(buffer, des_state);\n"; >>>>> } >>>>> ret ~= "\t}\n"; >>>>> } >>>>> return ret; >>>>> } >>>>> >>>>> >>>>> class ProxyClass(alias C, string methodstr) { >>>>> ubyte[] buffer; >>>>> mixin(ProxyMethods_mixin!(C,methodstr)()); >>>>> pragma(msg, "class ProxyClass!("~C.stringof~", \""~ >>>>> methodstr ~"\") {\n\tubyte[] buffer;\n SerializerState s_state;\n >>>>> DeserializerState des_state;\n this() {s_state = new >>>>> SerializerState(); des_state = new DeserializerState(); }\n\n"~ >>>>> ProxyMethods_mixin! (C,methodstr)() ~"\n}\n"); >>>>> >>>>> } >>>>> >>>>> void main() { >>>>> auto pc = new ProxyClass!(A, cast(string)"getInt setInt >>>>> getString"); >>>>> writefln("ProxyClass: "~ pc.getString("asdf")); >>>>> } >>>>> >>>>> >>>> That code is screaming for some macros. Or variable interpolation at >>>> least. >>>> >>>> --bb >>> Where would you propose that one would use 'macro'? >>> >>> >> It doesn't exist yet, so there's not much you can do about it. >> Unfortunately code that generates code in D pretty much has to look >> like what you wrote there. I'm just saying it's not a lot of fun to >> read such code. Compare with Lisp macros that are almost as readable >> as regular Lisp functions. >> >> Or maybe instead of macros, what's needed is variable interpolation >> like Perl has. Meaning you can embed a variable inside a string. (e.g. >> http://www.perlmeme.org/howtos/using_perl/interpolation.html) If one >> could write something like >> >> ret ~= "\t$ReturnType!(t).stringof splitFuncs(methodstr)[i](" >> >> It would at least look a bit nicer than >> >> ret ~= "\t"~ReturnType!(t).stringof ~" >> "~splitFuncs(methodstr)[i]~"("; >> >> with all the ~" "~ everywhere. >> >> --bb > > I did a blog post about that. > > http://while-nan.blogspot.com/2007/06/mixins-ctfe-and-shell-style- variable.html > > For reference, you could (with a few modifications) make it look like > this: > > mixin(ctsub(` > ret ~= "\t${ReturnType!(t).stringof} splitFuncs(methodstr)[i](" > `)); > > Not perfect, but perhaps slightly more readable. I don't remember how > robust the parsing logic was, though.
hm, that does look neater. I might use that. main reason I posted though was because I couldn't find anything about how to do this sort of thing in D that was straight forward, the code as I posted it should hopefully be easy for people learning D ctfe programming. ~= is easy to understand, and keeps the code small, only relying on the standard library. Thanks Rory