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.