digitalmars.D - Structs implementing interfaces in D1
- Tom S (99/99) Feb 09 2009 /** Just a simple hack to have interfaces implemented by structs ...
- Chad J (3/132) Feb 09 2009 Hawt!
- Tom S (8/11) Feb 09 2009 Should be doable, but with more pain and overhead (the generated
- BCS (2/9) Feb 09 2009 where is the vtable pointer placed?
- Tom S (7/18) Feb 09 2009 In the struct instance. There will be one for each implemented
- Tim M (2/2) Feb 10 2009 Is there a reason to have structs instead of classes/objects to do
- Tom S (12/14) Feb 10 2009 Structs are more lightweight because they don't carry the 'monitor' and
- BCS (5/18) Feb 10 2009 I have long thought that you should be able to generate an interface out...
- grauzone (3/6) Feb 10 2009 Seriously, this is one of the only things that make me actually want to
- Ary Borenszweig (12/72) Feb 10 2009 Since a lot of compile stuff is done via strings, I wish you could do
- grauzone (3/81) Feb 10 2009 Macros are supposed to solve that. D1.0 even has a "macro" keyword, but
- bearophile (4/5) Feb 10 2009 Have you tried std.metastrings.Format!()? I use it quite often.
- Ary Borenszweig (16/20) Feb 10 2009 template Format(A...)
- Don (5/35) Feb 10 2009 More than a year ago, I created a CTFE function dollar() to do that.
- Ary Borenszweig (4/42) Feb 10 2009 That's cool!
-
Daniel Keep
(5/49)
Feb 10 2009
- Steven Schveighoffer (40/51) Feb 10 2009 very, very cool.
- Tom S (138/161) Feb 10 2009 It's only a vtable-pointer-per-instance cost, but I guess that's what
- Steven Schveighoffer (7/16) Feb 10 2009 :D
- BCS (2/2) Feb 10 2009 Hello Tom,
/** 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
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
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
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
BCS wrote:Hello Tom,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 freenodeThe 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
Hello Tom,BCS wrote:struct S { int i; int j; int k; int n; } S[5] sArr; ca I make an inerface out of sArr[2] ?Hello Tom,In the struct instance. There will be one for each implemented interface, just as with class instances.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
BCS wrote:Hello Tom,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 freenodeBCS wrote:struct S { int i; int j; int k; int n; } S[5] sArr; ca I make an inerface out of sArr[2] ?Hello Tom,In the struct instance. There will be one for each implemented interface, just as with class instances.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
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
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
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
Hello Tom,Tim M wrote: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.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.
Feb 10 2009
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
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 implementationSince 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
Ary Borenszweig wrote:Tom S escribió:Macros are supposed to solve that. D1.0 even has a "macro" keyword, but so far macros are just vaporware./** 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 implementationSince 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
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
bearophile wrote:Ary Borenszweig: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.res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;";Have you tried std.metastrings.Format!()? I use it quite often.
Feb 10 2009
Ary Borenszweig wrote:bearophile wrote: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.Ary Borenszweig: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.res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;";Have you tried std.metastrings.Format!()? I use it quite often.
Feb 10 2009
Don escribió:Ary Borenszweig wrote:That's cool! Yeah, the mixin(dollar( )) stuff is too long... But it's nice that this can be done at compile time. :-)bearophile wrote: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.Ary Borenszweig: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.res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;";Have you tried std.metastrings.Format!()? I use it quite often.
Feb 10 2009
Ary Borenszweig wrote:Don escribió:<joking> #define F(args...) mixin(dollar(args)) </joking> -- DanielAry Borenszweig wrote:That's cool! Yeah, the mixin(dollar( )) stuff is too long... But it's nice that this can be done at compile time. :-)bearophile wrote: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.Ary Borenszweig: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.res ~= res ~= "\n\treturn cast(${iface})cast(void*)&_iface_${iface}_vtbl;";Have you tried std.metastrings.Format!()? I use it quite often.
Feb 10 2009
"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
Steven Schveighoffer wrote:"Tom S" wroteThanks :)/** 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.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
"Tom S" wroteSteven Schveighoffer wrote::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) -SteveSince 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]
Feb 10 2009
Hello Tom, if you want I can get you access to add this to scrapple.
Feb 10 2009
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
Hello Tom,h3r3tichave fun, watch peoples toes, yada yada yada <G>
Feb 10 2009