www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Structs implementing interfaces in D1

reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
/** Just a simple hack to have interfaces implemented by structs ... 
because I can :P
Tested on DMD 1.039 [win32], GDC (various versions) [linux32] and 
codepad.org

Output:

Entering main
Foo.func1 called ; val1 = 3.141590, val2 = Hello, world!
Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
Foo.func3 called ; val1 = 3.141590, val2 = Hello, world! ; p1 = 123, p2 
= some parameter
Leaving main
*/


module structIface;

extern (C) int printf(char*, ...);



// ---- evil implementation
char[] structImplementInterface(char[] iface, char[][] funcs) {
	char[] res = "private {";
	res ~= \n"alias typeof(*this) _iface_"~iface~"_ThisType;";

	foreach (func; funcs) {
		res ~= \n"static assert (is(typeof(&"~iface~".init."~func~")),
			`The interface "~iface~" doesn't declare a '"~func~"' function, `
			`thus struct `~_iface_"~iface~"_ThisType.stringof~` cannot implement 
it`);";
		res ~= \n"static void _iface_" ~ iface ~ "_func_" ~ func ~ "() {";
		version (GNU) {
			res ~= \n\t"asm { naked; sub dword ptr 4[ESP], _iface_" ~ iface ~ 
"_vtbl.offsetof; jmp " ~ func ~ "; }";
		} else {
			res ~= \n\t"asm { naked; sub EAX, _iface_" ~ iface ~ "_vtbl.offsetof; 
jmp " ~ func ~ "; }";
		}
		res ~= \n"}";
	}
	res ~= \n ~ iface ~ " as" ~ iface ~ "() {";
	res ~= \n\t"return cast("~iface~")cast(void*)&_iface_" ~ iface ~ "_vtbl;";
	res ~= \n"}";

	res ~= \n"void** _iface_" ~ iface ~ "_vtbl = cast(void**)([";
	res ~= \n\t"null";		// for classinfo
	foreach (func; funcs) {
		res ~= ",\n\tcast(void*)&Foo._iface_" ~ iface ~ "_func_" ~ func;
	}
	res ~= \n"]).ptr;";
	res ~= "}";

	return res;
}
// ---- end of the evil implementation


interface IFoo {
	void func1();
	void func2();
}

interface IBar {
	void func2();
	void func3(int, char[]);
}


struct Foo {
	float val1;
	char[] val2;

	void func1() {
		printf("Foo.func1 called ; val1 = %f, val2 = %.*s"\n, val1, val2);
	}

	void func2() {
		printf("Foo.func2 called ; val1 = %f, val2 = %.*s"\n, val1, val2);
	}

	void func3(int p1, char[] p2) {
		printf("Foo.func3 called ; val1 = %f, val2 = %.*s ; p1 = %d, p2 = 
%.*s"\n, val1, val2, p1, p2);
	}

	mixin(structImplementInterface("IFoo", ["func1", "func2"]));
	mixin(structImplementInterface("IBar", ["func2", "func3"]));
}


void main() {
	printf("Entering main"\n);

	Foo f;
	f.val1 = 3.14159f;
	f.val2 = "Hello, world!";

	IFoo fi = f.asIFoo;
	fi.func1();
	fi.func2();

	IBar bi = f.asIBar;
	bi.func2();
	bi.func3(123, "some parameter");

	printf("Leaving main"\n);
}


/**
The concept is pretty simple. The mixin creates a vtable which points to 
a set of generated functions of the form { adjust the 'this' ptr; jump 
to the real function; }. Finally, the "InterfaceName asInterfaceName()" 
functions generated inside the struct return pointers to the 
corresponding vtables.

Now this would be so much nicer if there was a way to iterate the 
functions of an interface at compile time in D1... Can we have D1 plus 
__traits? Pleaaaase? Pretty please with a cherry on top?

Thanks to downs, Hxal and Jarrett!
*/


-- 
Tomasz Stachowiak
http://h3.team0xf.com/
h3/h3r3tic on #D freenode
Feb 09 2009
next sibling parent reply Chad J <gamerchad __spam.is.bad__gmail.com> writes:
Tom S wrote:
 /** Just a simple hack to have interfaces implemented by structs ...
 because I can :P
 Tested on DMD 1.039 [win32], GDC (various versions) [linux32] and
 codepad.org
 
 Output:
 
 Entering main
 Foo.func1 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func3 called ; val1 = 3.141590, val2 = Hello, world! ; p1 = 123, p2
 = some parameter
 Leaving main
 */
 
 
 module structIface;
 
 extern (C) int printf(char*, ...);
 
 
 
 // ---- evil implementation
 char[] structImplementInterface(char[] iface, char[][] funcs) {
     char[] res = "private {";
     res ~= \n"alias typeof(*this) _iface_"~iface~"_ThisType;";
 
     foreach (func; funcs) {
         res ~= \n"static assert (is(typeof(&"~iface~".init."~func~")),
             `The interface "~iface~" doesn't declare a '"~func~"'
 function, `
             `thus struct `~_iface_"~iface~"_ThisType.stringof~` cannot
 implement it`);";
         res ~= \n"static void _iface_" ~ iface ~ "_func_" ~ func ~ "() {";
         version (GNU) {
             res ~= \n\t"asm { naked; sub dword ptr 4[ESP], _iface_" ~
 iface ~ "_vtbl.offsetof; jmp " ~ func ~ "; }";
         } else {
             res ~= \n\t"asm { naked; sub EAX, _iface_" ~ iface ~
 "_vtbl.offsetof; jmp " ~ func ~ "; }";
         }
         res ~= \n"}";
     }
     res ~= \n ~ iface ~ " as" ~ iface ~ "() {";
     res ~= \n\t"return cast("~iface~")cast(void*)&_iface_" ~ iface ~
 "_vtbl;";
     res ~= \n"}";
 
     res ~= \n"void** _iface_" ~ iface ~ "_vtbl = cast(void**)([";
     res ~= \n\t"null";        // for classinfo
     foreach (func; funcs) {
         res ~= ",\n\tcast(void*)&Foo._iface_" ~ iface ~ "_func_" ~ func;
     }
     res ~= \n"]).ptr;";
     res ~= "}";
 
     return res;
 }
 // ---- end of the evil implementation
 
 
 interface IFoo {
     void func1();
     void func2();
 }
 
 interface IBar {
     void func2();
     void func3(int, char[]);
 }
 
 
 struct Foo {
     float val1;
     char[] val2;
 
     void func1() {
         printf("Foo.func1 called ; val1 = %f, val2 = %.*s"\n, val1, val2);
     }
 
     void func2() {
         printf("Foo.func2 called ; val1 = %f, val2 = %.*s"\n, val1, val2);
     }
 
     void func3(int p1, char[] p2) {
         printf("Foo.func3 called ; val1 = %f, val2 = %.*s ; p1 = %d, p2
 = %.*s"\n, val1, val2, p1, p2);
     }
 
     mixin(structImplementInterface("IFoo", ["func1", "func2"]));
     mixin(structImplementInterface("IBar", ["func2", "func3"]));
 }
 
 
 void main() {
     printf("Entering main"\n);
 
     Foo f;
     f.val1 = 3.14159f;
     f.val2 = "Hello, world!";
 
     IFoo fi = f.asIFoo;
     fi.func1();
     fi.func2();
 
     IBar bi = f.asIBar;
     bi.func2();
     bi.func3(123, "some parameter");
 
     printf("Leaving main"\n);
 }
 
 
 /**
 The concept is pretty simple. The mixin creates a vtable which points to
 a set of generated functions of the form { adjust the 'this' ptr; jump
 to the real function; }. Finally, the "InterfaceName asInterfaceName()"
 functions generated inside the struct return pointers to the
 corresponding vtables.
 
 Now this would be so much nicer if there was a way to iterate the
 functions of an interface at compile time in D1... Can we have D1 plus
 __traits? Pleaaaase? Pretty please with a cherry on top?
 
 Thanks to downs, Hxal and Jarrett!
 */
 
 

Hawt! I take it this can be done without assembly?
Feb 09 2009
parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Chad J wrote:
 Hawt!
 
 I take it this can be done without assembly?

Should be doable, but with more pain and overhead (the generated functions would need to pass all parameters to the real ones and you'd have to take care of ref-ness). -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 09 2009
prev sibling next sibling parent reply BCS <none anon.com> writes:
Hello Tom,

 The concept is pretty simple. The mixin creates a vtable which points
 to
 a set of generated functions of the form { adjust the 'this' ptr; jump
 to the real function; }. Finally, the "InterfaceName
 asInterfaceName()"
 functions generated inside the struct return pointers to the
 corresponding vtables.

where is the vtable pointer placed?
Feb 09 2009
parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
BCS wrote:
 Hello Tom,
 
 The concept is pretty simple. The mixin creates a vtable which points
 to
 a set of generated functions of the form { adjust the 'this' ptr; jump
 to the real function; }. Finally, the "InterfaceName
 asInterfaceName()"
 functions generated inside the struct return pointers to the
 corresponding vtables.

where is the vtable pointer placed?

In the struct instance. There will be one for each implemented interface, just as with class instances. -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 09 2009
parent reply BCS <none anon.com> writes:
Hello Tom,

 BCS wrote:
 
 Hello Tom,
 
 The concept is pretty simple. The mixin creates a vtable which
 points
 to
 a set of generated functions of the form { adjust the 'this' ptr;
 jump
 to the real function; }. Finally, the "InterfaceName
 asInterfaceName()"
 functions generated inside the struct return pointers to the
 corresponding vtables.


interface, just as with class instances.

struct S { int i; int j; int k; int n; } S[5] sArr; ca I make an inerface out of sArr[2] ?
Feb 09 2009
next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
BCS wrote:
 Hello Tom,
 
 BCS wrote:

 Hello Tom,

 The concept is pretty simple. The mixin creates a vtable which
 points
 to
 a set of generated functions of the form { adjust the 'this' ptr;
 jump
 to the real function; }. Finally, the "InterfaceName
 asInterfaceName()"
 functions generated inside the struct return pointers to the
 corresponding vtables.


interface, just as with class instances.

struct S { int i; int j; int k; int n; } S[5] sArr; ca I make an inerface out of sArr[2] ?

Yes, there would be ways to do it, but the trick I've presented requires that S gets an extra field - the vtable pointer. -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 09 2009
prev sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
BCS wrote:
 struct S { int i; int j; int k; int n; }
 
 
 S[5] sArr;
 
 ca I make an inerface out of sArr[2] ?

The new version of my code can do it. Of course you'd still need some functions in S and an actual interface there ;) -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 10 2009
prev sibling next sibling parent reply "Tim M" <a b.com> writes:
Is there a reason to have structs instead of classes/objects to do  
whatever you use them for or is that besides the point?
Feb 10 2009
parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Tim M wrote:
 Is there a reason to have structs instead of classes/objects to do 
 whatever you use them for or is that besides the point?

Structs are more lightweight because they don't carry the 'monitor' and vtable pointer. It's also easier to create them on the stack e.g. in arrays. But my code was mostly a proof of concept implementation, since many folks did not realize that structs might implement interfaces too. It would also be possible to do more exotic stuff, like constructing interface 'instances' out of free functions at runtime. My point was to show one possible way. -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 10 2009
parent BCS <none anon.com> writes:
Hello Tom,

 Tim M wrote:
 
 Is there a reason to have structs instead of classes/objects to do
 whatever you use them for or is that besides the point?
 

and vtable pointer. It's also easier to create them on the stack e.g. in arrays. But my code was mostly a proof of concept implementation, since many folks did not realize that structs might implement interfaces too. It would also be possible to do more exotic stuff, like constructing interface 'instances' out of free functions at runtime. My point was to show one possible way.

I have long thought that you should be able to generate an interface out of nested functions. All of this would be trivial if COM interop could be dumped and interfaces done as a context/vtbl pairs.
Feb 10 2009
prev sibling next sibling parent grauzone <none example.net> writes:
 Now this would be so much nicer if there was a way to iterate the 
 functions of an interface at compile time in D1... Can we have D1 plus 
 __traits? Pleaaaase? Pretty please with a cherry on top?

Seriously, this is one of the only things that make me actually want to use D1.0. Seems like I'm not the only one. Therefore, it will never happen. Walter wants people to use D2.0, of course.
Feb 10 2009
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Tom S escribió:
 /** Just a simple hack to have interfaces implemented by structs ... 
 because I can :P
 Tested on DMD 1.039 [win32], GDC (various versions) [linux32] and 
 codepad.org
 
 Output:
 
 Entering main
 Foo.func1 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func3 called ; val1 = 3.141590, val2 = Hello, world! ; p1 = 123, p2 
 = some parameter
 Leaving main
 */
 
 
 module structIface;
 
 extern (C) int printf(char*, ...);
 
 
 
 // ---- evil implementation
 char[] structImplementInterface(char[] iface, char[][] funcs) {
     char[] res = "private {";
     res ~= \n"alias typeof(*this) _iface_"~iface~"_ThisType;";
 
     foreach (func; funcs) {
         res ~= \n"static assert (is(typeof(&"~iface~".init."~func~")),
             `The interface "~iface~" doesn't declare a '"~func~"' 
 function, `
             `thus struct `~_iface_"~iface~"_ThisType.stringof~` cannot 
 implement it`);";
         res ~= \n"static void _iface_" ~ iface ~ "_func_" ~ func ~ "() {";
         version (GNU) {
             res ~= \n\t"asm { naked; sub dword ptr 4[ESP], _iface_" ~ 
 iface ~ "_vtbl.offsetof; jmp " ~ func ~ "; }";
         } else {
             res ~= \n\t"asm { naked; sub EAX, _iface_" ~ iface ~ 
 "_vtbl.offsetof; jmp " ~ func ~ "; }";
         }
         res ~= \n"}";
     }
     res ~= \n ~ iface ~ " as" ~ iface ~ "() {";
     res ~= \n\t"return cast("~iface~")cast(void*)&_iface_" ~ iface ~ 
 "_vtbl;";
     res ~= \n"}";
 
     res ~= \n"void** _iface_" ~ iface ~ "_vtbl = cast(void**)([";
     res ~= \n\t"null";        // for classinfo
     foreach (func; funcs) {
         res ~= ",\n\tcast(void*)&Foo._iface_" ~ iface ~ "_func_" ~ func;
     }
     res ~= \n"]).ptr;";
     res ~= "}";
 
     return res;
 }
 // ---- end of the evil implementation

Since a lot of compile stuff is done via strings, I wish you could do somehing like this in D (like in PHP): res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;"; Much nicer and understandable. And I think it's not hard to implement: when doing semantic for a string like that, parse it and search the variables in the current scope and upwards (as a usual search). For this to work, primitives should have a string representation, and if it's a class or struct, then toString() is invoked. It's really hard to follow a code like that, with thousands of "~". And maybe this should only be done for strings enclosed in `, so that the search is not always done.
Feb 10 2009
next sibling parent grauzone <none example.net> writes:
Ary Borenszweig wrote:
 Tom S escribió:
 /** Just a simple hack to have interfaces implemented by structs ... 
 because I can :P
 Tested on DMD 1.039 [win32], GDC (various versions) [linux32] and 
 codepad.org

 Output:

 Entering main
 Foo.func1 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func2 called ; val1 = 3.141590, val2 = Hello, world!
 Foo.func3 called ; val1 = 3.141590, val2 = Hello, world! ; p1 = 123, 
 p2 = some parameter
 Leaving main
 */


 module structIface;

 extern (C) int printf(char*, ...);



 // ---- evil implementation
 char[] structImplementInterface(char[] iface, char[][] funcs) {
     char[] res = "private {";
     res ~= \n"alias typeof(*this) _iface_"~iface~"_ThisType;";

     foreach (func; funcs) {
         res ~= \n"static assert (is(typeof(&"~iface~".init."~func~")),
             `The interface "~iface~" doesn't declare a '"~func~"' 
 function, `
             `thus struct `~_iface_"~iface~"_ThisType.stringof~` cannot 
 implement it`);";
         res ~= \n"static void _iface_" ~ iface ~ "_func_" ~ func ~ "() 
 {";
         version (GNU) {
             res ~= \n\t"asm { naked; sub dword ptr 4[ESP], _iface_" ~ 
 iface ~ "_vtbl.offsetof; jmp " ~ func ~ "; }";
         } else {
             res ~= \n\t"asm { naked; sub EAX, _iface_" ~ iface ~ 
 "_vtbl.offsetof; jmp " ~ func ~ "; }";
         }
         res ~= \n"}";
     }
     res ~= \n ~ iface ~ " as" ~ iface ~ "() {";
     res ~= \n\t"return cast("~iface~")cast(void*)&_iface_" ~ iface ~ 
 "_vtbl;";
     res ~= \n"}";

     res ~= \n"void** _iface_" ~ iface ~ "_vtbl = cast(void**)([";
     res ~= \n\t"null";        // for classinfo
     foreach (func; funcs) {
         res ~= ",\n\tcast(void*)&Foo._iface_" ~ iface ~ "_func_" ~ func;
     }
     res ~= \n"]).ptr;";
     res ~= "}";

     return res;
 }
 // ---- end of the evil implementation

Since a lot of compile stuff is done via strings, I wish you could do somehing like this in D (like in PHP): res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;"; Much nicer and understandable. And I think it's not hard to implement: when doing semantic for a string like that, parse it and search the variables in the current scope and upwards (as a usual search). For this to work, primitives should have a string representation, and if it's a class or struct, then toString() is invoked. It's really hard to follow a code like that, with thousands of "~". And maybe this should only be done for strings enclosed in `, so that the search is not always done.

Macros are supposed to solve that. D1.0 even has a "macro" keyword, but so far macros are just vaporware.
Feb 10 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Ary Borenszweig:
 res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;";

Have you tried std.metastrings.Format!()? I use it quite often. Bye, bearophile
Feb 10 2009
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
bearophile wrote:
 Ary Borenszweig:
 res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;";

Have you tried std.metastrings.Format!()? I use it quite often.

template Format(A...) Formats constants into a string at compile time. Analogous to std.string.format(). Parameters: A = tuple of *constants*, which can be strings, characters, or integral values. I want it in runtime. I basically want this: foo = `Hello ${var}!`; or foo = `Hello $var!`; to be the same as: foo = `Hello ` ~ var ~ `!`; You can see in this trivial example how readability is improved (and you type less), and in a bigger example (like the one in this thread) it should be better.
Feb 10 2009
parent reply Don <nospam nospam.com> writes:
Ary Borenszweig wrote:
 bearophile wrote:
 Ary Borenszweig:
 res ~= res ~= "\n\treturn 
 cast(${iface})cast(void*)&_iface_${iface}_vtbl;";

Have you tried std.metastrings.Format!()? I use it quite often.

template Format(A...) Formats constants into a string at compile time. Analogous to std.string.format(). Parameters: A = tuple of *constants*, which can be strings, characters, or integral values. I want it in runtime. I basically want this: foo = `Hello ${var}!`; or foo = `Hello $var!`; to be the same as: foo = `Hello ` ~ var ~ `!`; You can see in this trivial example how readability is improved (and you type less), and in a bigger example (like the one in this thread) it should be better.

More than a year ago, I created a CTFE function dollar() to do that. It's a very simple. No language support is required. foo = mixin(dollar("Hello $var!")); We just need a way to get rid of the "mixin(" bit.
Feb 10 2009
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Don escribió:
 Ary Borenszweig wrote:
 bearophile wrote:
 Ary Borenszweig:
 res ~= res ~= "\n\treturn 
 cast(${iface})cast(void*)&_iface_${iface}_vtbl;";

Have you tried std.metastrings.Format!()? I use it quite often.

template Format(A...) Formats constants into a string at compile time. Analogous to std.string.format(). Parameters: A = tuple of *constants*, which can be strings, characters, or integral values. I want it in runtime. I basically want this: foo = `Hello ${var}!`; or foo = `Hello $var!`; to be the same as: foo = `Hello ` ~ var ~ `!`; You can see in this trivial example how readability is improved (and you type less), and in a bigger example (like the one in this thread) it should be better.

More than a year ago, I created a CTFE function dollar() to do that. It's a very simple. No language support is required. foo = mixin(dollar("Hello $var!")); We just need a way to get rid of the "mixin(" bit.

That's cool! Yeah, the mixin(dollar( )) stuff is too long... But it's nice that this can be done at compile time. :-)
Feb 10 2009
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Ary Borenszweig wrote:
 Don escribió:
 Ary Borenszweig wrote:
 bearophile wrote:
 Ary Borenszweig:
 res ~= res ~= "\n\treturn
 cast(${iface})cast(void*)&_iface_${iface}_vtbl;";

Have you tried std.metastrings.Format!()? I use it quite often.

template Format(A...) Formats constants into a string at compile time. Analogous to std.string.format(). Parameters: A = tuple of *constants*, which can be strings, characters, or integral values. I want it in runtime. I basically want this: foo = `Hello ${var}!`; or foo = `Hello $var!`; to be the same as: foo = `Hello ` ~ var ~ `!`; You can see in this trivial example how readability is improved (and you type less), and in a bigger example (like the one in this thread) it should be better.

More than a year ago, I created a CTFE function dollar() to do that. It's a very simple. No language support is required. foo = mixin(dollar("Hello $var!")); We just need a way to get rid of the "mixin(" bit.

That's cool! Yeah, the mixin(dollar( )) stuff is too long... But it's nice that this can be done at compile time. :-)

<joking> #define F(args...) mixin(dollar(args)) </joking> -- Daniel
Feb 10 2009
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Tom S" wrote
 /**
 The concept is pretty simple. The mixin creates a vtable which points to a 
 set of generated functions of the form { adjust the 'this' ptr; jump to 
 the real function; }. Finally, the "InterfaceName asInterfaceName()" 
 functions generated inside the struct return pointers to the corresponding 
 vtables.

 Now this would be so much nicer if there was a way to iterate the 
 functions of an interface at compile time in D1... Can we have D1 plus 
 __traits? Pleaaaase? Pretty please with a cherry on top?

 Thanks to downs, Hxal and Jarrett!
 */

very, very cool. However, I don't like the vtable-per-instance penalty. Since structs are generally stack variables, why not make the interface a separate stack variable? Most of the time, you are interested in passing a struct to a function via an interface so the function can use the interface but not save a reference to it. i.e.: interface I { int x(); } int foo(I i) { return i.x; } struct S { int x() { return 1;} } struct Impl(Intf, Struct) { Struct *thisptr; // template-mixin-fu goes here Intf opCast() {...} } void bar() { S s; auto si = Impl!(I, S)(s); foo(cast(I)si); } With this, you could probably build a composite struct which has the vtable and instance included inside one entity, so you can get back to the original idea if you want to say put things in an array. There could even be a template for that too. This is sort of how I envisioned compiler-supported struct interfaces working anyways (generate a stack "interface" every time you use the struct as its implemented interface). -Steve
Feb 10 2009
parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Steven Schveighoffer wrote:
 "Tom S" wrote
 /**
 The concept is pretty simple. The mixin creates a vtable which points to a 
 set of generated functions of the form { adjust the 'this' ptr; jump to 
 the real function; }. Finally, the "InterfaceName asInterfaceName()" 
 functions generated inside the struct return pointers to the corresponding 
 vtables.

 Now this would be so much nicer if there was a way to iterate the 
 functions of an interface at compile time in D1... Can we have D1 plus 
 __traits? Pleaaaase? Pretty please with a cherry on top?

 Thanks to downs, Hxal and Jarrett!
 */

very, very cool.

Thanks :)
 However, I don't like the vtable-per-instance penalty.

It's only a vtable-pointer-per-instance cost, but I guess that's what you meant.
 Since structs are generally stack variables, why not make the interface a 
 separate stack variable?  Most of the time, you are interested in passing a 
 struct to a function via an interface so the function can use the interface 
 but not save a reference to it.
 
 (snip)

Great idea! :D I couldn't stop myself from implementing it: module structIface; extern (C) int printf(char*, ...); // ---- evil implementation char[] structImplementInterface(char[] strname, char[] iface, char[][] funcs) { char[] res = "private {"; res ~= \n"alias typeof(*this) _iface_"~iface~"_ThisType;"; foreach (func; funcs) { res ~= \n"static assert (is(typeof(&"~iface~".init."~func~")), `The interface "~iface~" doesn't declare a '"~func~"' function, ` `thus struct `~_iface_"~iface~"_ThisType.stringof~` cannot implement it`);"; res ~= \n"static void _iface_" ~ iface ~ "_func_" ~ func ~ "() {"; res ~= \n\t"asm { naked; "; version (GNU) { res ~= "push EAX; mov EAX, dword ptr 8[ESP]; "; } res ~= "sub EAX, _iface_" ~ iface ~ "_vtbl.offsetof; mov EAX, [EAX]; "; version (GNU) { res ~= "mov dword ptr 8[ESP], EAX; pop EAX; "; } res ~= "jmp " ~ strname ~ "." ~ func ~ "; }"; res ~= \n"}"; } res ~= \n ~ iface ~ " as" ~ iface ~ "() {"; res ~= \n\t"return cast("~iface~")cast(void*)&_iface_" ~ iface ~ "_vtbl;"; res ~= \n"}"; res ~= \n"void** _iface_" ~ iface ~ "_vtbl = cast(void**)(["; res ~= \n\t"null"; // for classinfo foreach (func; funcs) { res ~= ",\n\tcast(void*)&_iface_" ~ iface ~ "_func_" ~ func; } res ~= \n"]).ptr;"; res ~= "}"; return res; } // ---- end of the evil implementation interface IFoo { void func1(); void func2(); } interface IBar { void func2(); void func3(int, char[]); } char[][] splitFuncs(char[] str) { char[][] 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; } struct Impl_(Intf, Struct, char[] funcs) { Struct* thisptr; mixin(structImplementInterface(Struct.stringof, Intf.stringof, splitFuncs(funcs))); static Impl_ opCall(ref Struct s) { Impl_ res; res.thisptr = &s; return res; } Intf opCast() { mixin("return as"~Intf.stringof~"();"); } } template Impl(Intf, char[] funcs) { Impl_!(Intf, Struct, funcs) Impl(Struct)(ref Struct s) { return Impl_!(Intf, Struct, funcs)(s); } } struct Foo { float val1; char[] val2; void func1() { printf("Foo.func1 called ; val1 = %f, val2 = %.*s"\n, val1, val2); } void func2() { printf("Foo.func2 called ; val1 = %f, val2 = %.*s"\n, val1, val2); } void func3(int p1, char[] p2) { printf("Foo.func3 called ; val1 = %f, val2 = %.*s ; p1 = %d, p2 = %.*s"\n, val1, val2, p1, p2); } } // ---- Steven's example interface I { int x(); } int foo(I i) { return i.x; } struct S { int x() { return 123456; } } void bar() { S s; auto si = Impl!(I, "x")(s); int res = foo(cast(I)si); printf("foo(si) returned %d"\n, res); } // ---- void main() { printf("Entering main"\n); Foo f; f.val1 = 3.14159f; f.val2 = "Hello, world!"; auto sfi = Impl!(IFoo, "func1, func2")(f); auto fi = cast(IFoo)sfi; fi.func1(); fi.func2(); auto sbi = Impl!(IBar, "func2, func3")(f); auto bi = cast(IBar)sbi; bi.func2(); bi.func3(123, "some parameter"); bar(); printf("Leaving main"\n); } -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 10 2009
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Tom S" wrote
 Steven Schveighoffer wrote:
 Since structs are generally stack variables, why not make the interface a 
 separate stack variable?  Most of the time, you are interested in passing 
 a struct to a function via an interface so the function can use the 
 interface but not save a reference to it.

 (snip)

Great idea! :D I couldn't stop myself from implementing it: [snip]

:D It would be really cool if a huge pile of money could appear before me /me waits patiently Seriously, you have made my day brighter, now to convince Walter... Alas, I'm sure we'll never see this as a compiler feature in D1 (in dmd at least) -Steve
Feb 10 2009
prev sibling parent reply BCS <none anon.com> writes:
Hello Tom,

if you want I can get you access to add this to scrapple.
Feb 10 2009
parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
BCS wrote:
 Hello Tom,
 
 if you want I can get you access to add this to scrapple.

I guess that would be cool :) My dsource user name is, as usual, 'h3r3tic'. -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 10 2009
parent BCS <none anon.com> writes:
Hello Tom,

 h3r3tic
 

have fun, watch peoples toes, yada yada yada <G>
Feb 10 2009