Semi Automated Object wrapping
Daniel Keep
daniel.keep.lists at gmail.com
Thu Aug 13 07:16:39 PDT 2009
Bill Baxter wrote:
> On Thu, Aug 13, 2009 at 12:43 AM, Rory McGuire<rjmcguire at 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<rjmcguire at 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,
>>>> rjmcguire at 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.
More information about the Digitalmars-d-learn
mailing list