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