Semi Automated Object wrapping

Rory McGuire rjmcguire at gmail.com
Thu Aug 13 11:32:46 PDT 2009


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<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.

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


More information about the Digitalmars-d-learn mailing list