Semi Automated Object wrapping

Bill Baxter wbaxter at gmail.com
Thu Aug 13 06:26:24 PDT 2009


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


More information about the Digitalmars-d-learn mailing list