www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Semi Automated Object wrapping

reply Rory McGuire <rjmcguire gmail.com> writes:
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 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"));
}
Aug 12 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Wed, Aug 12, 2009 at 4:52 PM, Rory McGuire<rjmcguire 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

 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
 // author: Rory McGuire, rjmcguire 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) {
 =A0 =A0string[] res;
 =A0 =A0while (str.length > 0) {
 =A0 =A0 =A0 =A0while (str.length > 0 && (' ' =3D=3D str[0] || ',' =3D=3D =
str[0])) {
 =A0 =A0 =A0 =A0 =A0 =A0str =3D str[1..$];
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0int to =3D 0;
 =A0 =A0 =A0 =A0for (; to < str.length && str[to] !=3D ' ' && str[to] !=3D=
','; ++to)
 {}
 =A0 =A0 =A0 =A0if (to > 0) {
 =A0 =A0 =A0 =A0 =A0 =A0res ~=3D str[0..to];
 =A0 =A0 =A0 =A0 =A0 =A0str =3D str[to..$];
 =A0 =A0 =A0 =A0}
 =A0 =A0}
 =A0 =A0return res;
 }

 string MethodTypeTuple_mixin(alias a)(string[] methods) {
 =A0 =A0 =A0 =A0string ret =3D "TypeTuple!("~ "typeof(&C.init."~methods[0]=
~")";
 =A0 =A0 =A0 =A0foreach (method; methods[1..$]) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ",typeof(&C.init."~method~")";
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0ret ~=3D ")";
 =A0 =A0 =A0 =A0return ret;
 }



 // test case

 class A {
 =A0 =A0 =A0 =A0int a;
 =A0 =A0 =A0 =A0this(int a) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0this.a =3D a;
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0int getInt(string intname) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return a;
 =A0 =A0 =A0 =A0}

 =A0 =A0 =A0 =A0void setInt(int i) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0a =3D i;
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0string getString(string s) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return s ~"1234";
 =A0 =A0 =A0 =A0}
 }


 string ProxyMethods_mixin(alias C, string methodstr)() {
 =A0 =A0 =A0 =A0string ret;
 =A0 =A0 =A0 =A0foreach(i, t; mixin(MethodTypeTuple_mixin!(C)(splitFuncs
 (methodstr)))) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output function header
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t"~ReturnType!(t).stringof ~" "=
~ splitFuncs
 (methodstr)[i]~"(";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output first arg
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ParameterTypeTuple!(t)[0].stringo=
f~" arg";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output remainder of args
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0foreach (j, t1; ParameterTypeTuple!(t)[1..=
$]) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ","~t1.stringof~"
 arg"~std.metastrings.ToString!(j);
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output body
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ") {\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output serialization code
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// send method name
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\twritefln(\"serialize docall =
id\"); // the
 method call byte id\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\tbuffer ~=3D serialize!(strin=
g)(\""~splitFuncs
 (methodstr)[i]~"\", s_state); /+ the method name +/\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// send args
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\tbuffer ~=3D serialize!("~ Pa=
rameterTypeTuple!(t)
 [0].stringof~")(arg, s_state); /+ the first argument +/\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0foreach (j, t1; ParameterTypeTuple!(t)[1..=
$]) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\tbuffer ~=3D =
serialize!("~ t1.stringof
 ~")(arg"~ToString!(j)~", s_state); /+ argument "~ToString!(j)~" +/\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// receive return type
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0static if (!is(ReturnType!(t) =3D=3D void)=
) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\treturn deser=
ialize!("~ ReturnType!
 (t).stringof ~")(buffer, des_state);\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t}\n";
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0return ret;
 }


 class ProxyClass(alias C, string methodstr) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ubyte[] buffer;
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0mixin(ProxyMethods_mixin!(C,methodstr)());
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pragma(msg, "class ProxyClass!("~C.stringo=
f~", \""~
 methodstr ~"\") {\n\tubyte[] buffer;\n =A0SerializerState s_state;\n
 DeserializerState des_state;\n =A0this() {s_state =3D new SerializerState=
();
 des_state =3D new DeserializerState(); }\n\n"~ ProxyMethods_mixin!
 (C,methodstr)() ~"\n}\n");

 }

 void main() {
 =A0 =A0 =A0 =A0auto pc =3D new ProxyClass!(A, cast(string)"getInt setInt
 getString");
 =A0 =A0 =A0 =A0writefln("ProxyClass: "~ pc.getString("asdf"));
 }
That code is screaming for some macros. Or variable interpolation at least. --bb
Aug 12 2009
parent reply Rory McGuire <rjmcguire gmail.com> writes:
On Wed, 12 Aug 2009 17:03:17 -0700, Bill Baxter wrote:

 On Wed, Aug 12, 2009 at 4:52 PM, Rory McGuire<rjmcguire 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 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'?
Aug 13 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Thu, Aug 13, 2009 at 12:43 AM, Rory McGuire<rjmcguire 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 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

 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D // author: = Rory McGuire,
 rjmcguire 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) {
 =A0 =A0string[] res;
 =A0 =A0while (str.length > 0) {
 =A0 =A0 =A0 =A0while (str.length > 0 && (' ' =3D=3D str[0] || ',' =3D=
=3D str[0])) {
 =A0 =A0 =A0 =A0 =A0 =A0str =3D str[1..$];
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0int to =3D 0;
 =A0 =A0 =A0 =A0for (; to < str.length && str[to] !=3D ' ' && str[to] !=
=3D ',';
 =A0 =A0 =A0 =A0++to)
 {}
 =A0 =A0 =A0 =A0if (to > 0) {
 =A0 =A0 =A0 =A0 =A0 =A0res ~=3D str[0..to];
 =A0 =A0 =A0 =A0 =A0 =A0str =3D str[to..$];
 =A0 =A0 =A0 =A0}
 =A0 =A0}
 =A0 =A0return res;
 }

 string MethodTypeTuple_mixin(alias a)(string[] methods) {
 =A0 =A0 =A0 =A0string ret =3D "TypeTuple!("~
 =A0 =A0 =A0 =A0"typeof(&C.init."~methods[0]~")"; foreach (method;
 =A0 =A0 =A0 =A0methods[1..$]) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ",typeof(&C.init."~method~")";
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0ret ~=3D ")";
 =A0 =A0 =A0 =A0return ret;
 }



 // test case

 class A {
 =A0 =A0 =A0 =A0int a;
 =A0 =A0 =A0 =A0this(int a) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0this.a =3D a;
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0int getInt(string intname) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return a;
 =A0 =A0 =A0 =A0}

 =A0 =A0 =A0 =A0void setInt(int i) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0a =3D i;
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0string getString(string s) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return s ~"1234";
 =A0 =A0 =A0 =A0}
 }


 string ProxyMethods_mixin(alias C, string methodstr)() {
 =A0 =A0 =A0 =A0string ret;
 =A0 =A0 =A0 =A0foreach(i, t; mixin(MethodTypeTuple_mixin!(C)(splitFuncs
 (methodstr)))) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output function header
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t"~ReturnType!(t).stringof ~"=
"~
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0splitFuncs
 (methodstr)[i]~"(";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output first arg
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ParameterTypeTuple!(t)[0].strin=
gof~"
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0arg"; // output remainder of args
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0foreach (j, t1; ParameterTypeTuple!(t)[1=
..$]) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ","~t1.stringof=
~"
 arg"~std.metastrings.ToString!(j);
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output body
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D ") {\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// output serialization code
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// send method name
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\twritefln(\"serialize docal=
l id\");
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// the
 method call byte id\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\tbuffer ~=3D
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0serialize!(string)(\""~splitFuncs
 (methodstr)[i]~"\", s_state); /+ the method name +/\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// send args
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\tbuffer ~=3D serialize!("~
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ParameterTypeTuple!(t)
 [0].stringof~")(arg, s_state); /+ the first argument +/\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0foreach (j, t1; ParameterTypeTuple!(t)[1=
..$]) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\tbuffer ~=
=3D serialize!("~
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0t1.stringof
 ~")(arg"~ToString!(j)~", s_state); /+ argument "~ToString!(j)~" +/\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// receive return type
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0static if (!is(ReturnType!(t) =3D=3D voi=
d)) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t\treturn des=
erialize!("~
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ReturnType!
 (t).stringof ~")(buffer, des_state);\n";
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ret ~=3D "\t}\n";
 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0return ret;
 }


 class ProxyClass(alias C, string methodstr) {
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ubyte[] buffer;
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0mixin(ProxyMethods_mixin!(C,methodstr)()=
);
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pragma(msg, "class ProxyClass!("~C.strin=
gof~",
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0\""~
 methodstr ~"\") {\n\tubyte[] buffer;\n =A0SerializerState s_state;\n
 DeserializerState des_state;\n =A0this() {s_state =3D new
 SerializerState(); des_state =3D new DeserializerState(); }\n\n"~
 ProxyMethods_mixin! (C,methodstr)() ~"\n}\n");

 }

 void main() {
 =A0 =A0 =A0 =A0auto pc =3D new ProxyClass!(A, cast(string)"getInt setIn=
t
 getString");
 =A0 =A0 =A0 =A0writefln("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 ~=3D "\t$ReturnType!(t).stringof splitFuncs(methodstr)[i](" It would at least look a bit nicer than ret ~=3D "\t"~ReturnType!(t).stringof ~" "~splitFuncs(methodstr)[i]~"(= "; with all the ~" "~ everywhere. --bb
Aug 13 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Bill Baxter wrote:
 On Thu, Aug 13, 2009 at 12:43 AM, Rory McGuire<rjmcguire 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 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 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.
Aug 13 2009
parent Rory McGuire <rjmcguire gmail.com> writes:
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 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 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 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
Aug 13 2009
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Bill Baxter:
     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.
Currently you can do (I think it doesn't work well in D2 now): import std.metastrings: Format; ... ret ~= Format!("\t %s %s(", ReturnType!(t).stringof, splitFuncs(methodstr)[i]); Bye, bearophile
Aug 13 2009