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