digitalmars.D.learn - Programming to Interfaces simplification
- Frustrated (39/39) Feb 24 2014 http://dpaste.dzfl.pl/c25655e2dfe9
- Steven Schveighoffer (28/57) Feb 24 2014 Hm... classinfo (now typeid) should get the most derived type from an
- Frustrated (26/91) Feb 24 2014 Thanks. Now the correct type is known. One could, for example,
- Frustrated (19/19) Feb 24 2014 Fixed code with all combination examples:
- Steven Schveighoffer (9/105) Feb 24 2014 First, the vtable is not per-instance, so it doesn't detract too much fr...
http://dpaste.dzfl.pl/c25655e2dfe9 The code above simplifies using interfaces as the programming object. It allows one to program the derived classes as if they were not part of an abstraction by mapping the abstracted virtual methods to concrete methods. e.g., class WindowsGui : iGui { WindowsButton _button; property WindowsButton button(WindowsButton b) { return (_button = b); } mixin(Fixup!(WindowsGui, iButton, WindowsButton)); } instead of class WindowsGui : iGui { WindowsButton _button; property iButton button(iButton b) { assert(cast(WindowsButton)b !is null, `Invalid object type dependency mismatch! Type: `~b.classinfo.name~` Type Expected: WindowsButton`); auto bb = cast(WindowsButton)b; // do work with bb. } } (note the check and the cast are required for all usages of iButton if one wants to treat it as a WindowsButton. By using the mix, no checks and no casts are required. Much cleaner looking and less verbose code results, which is the whole point.) One problem with the template is that b.classinfo.name returns the interface instead of the actual class. In the example on dpaste, b.classinfo.name returns iButton, the base interface of LinuxButton. I want to display the actual class name that causes the problem(LinuxButton trying to be used where a WindowsButton goes). Obviously the Fixup template is not robust nor optimized for all cases but should handle most.
Feb 24 2014
On Mon, 24 Feb 2014 11:36:50 -0500, Frustrated <c1514843 drdrb.com> wrote:http://dpaste.dzfl.pl/c25655e2dfe9 The code above simplifies using interfaces as the programming object. It allows one to program the derived classes as if they were not part of an abstraction by mapping the abstracted virtual methods to concrete methods. e.g., class WindowsGui : iGui { WindowsButton _button; property WindowsButton button(WindowsButton b) { return (_button = b); } mixin(Fixup!(WindowsGui, iButton, WindowsButton)); } instead of class WindowsGui : iGui { WindowsButton _button; property iButton button(iButton b) { assert(cast(WindowsButton)b !is null, `Invalid object type dependency mismatch! Type: `~b.classinfo.name~` Type Expected: WindowsButton`); auto bb = cast(WindowsButton)b; // do work with bb. } }Nice work!One problem with the template is that b.classinfo.name returns the interface instead of the actual class.Hm... classinfo (now typeid) should get the most derived type from an instance. This may be a factor of it being an interface instance vs. a class instance. A simple test: Stevens-MacBook-Pro:~ steves$ cat testinterface.d import std.stdio; interface I { } class C : I { } void main() { I i = new C; writeln(typeid(i).name); writeln(typeid(cast(Object)i).name); } Stevens-MacBook-Pro:~ steves$ ./testinterface testinterface.I testinterface.C Looks like that is the case. Note that classinfo is not part of the language any more, and will likely be deprecated. typeid is the correct mechanism to get the TypeInfo of a derived class. I'm thinking this is incorrect, typeid should get the derived class type IMO. It shouldn't be that difficult or costly for the compiler to do. -Steve
Feb 24 2014
On Monday, 24 February 2014 at 18:59:32 UTC, Steven Schveighoffer wrote:On Mon, 24 Feb 2014 11:36:50 -0500, Frustrated <c1514843 drdrb.com> wrote:Thanks. Now the correct type is known. One could, for example, look for wrappers/adapters/etc to get a linuxbutton into a windowsbutton(if so desired, throw a well informed error, etc. Hopefully though, now you see the point. It is a runtime contract that you make since you can't specify it at compile time(since D doesn't supports it). In any case the mixin needs some work and testing. Would just like to get the proper class name for the type. It is, at the very least, a proof of concept. Unfortunately the main downside is the vtable is twice as big. Final or static methods could be used for this. If the compiler could manage such a system it could do the job better. (There would be no extra functions needed and the calls would be faster) The issue is this: I want to program to interfaces(requiring the class(WindowsGui) to use base interfaces(iButton) to satisfy the interface. The problem is this compile time contract is to generic and makes coding the classes more verbose. The mixin reduces the verbosity and provides a runtime contract(we enforce it using asserts in this case). The benefit of the mixin is that coding WindowsGui is simpler and more direct and it's dependency on WindowsButton(not iButton) is known at compile time(But enforced at runtime by assert). Would you not agree? Everything else is essentially identical.http://dpaste.dzfl.pl/c25655e2dfe9 The code above simplifies using interfaces as the programming object. It allows one to program the derived classes as if they were not part of an abstraction by mapping the abstracted virtual methods to concrete methods. e.g., class WindowsGui : iGui { WindowsButton _button; property WindowsButton button(WindowsButton b) { return (_button = b); } mixin(Fixup!(WindowsGui, iButton, WindowsButton)); } instead of class WindowsGui : iGui { WindowsButton _button; property iButton button(iButton b) { assert(cast(WindowsButton)b !is null, `Invalid object type dependency mismatch! Type: `~b.classinfo.name~` Type Expected: WindowsButton`); auto bb = cast(WindowsButton)b; // do work with bb. } }Nice work!One problem with the template is that b.classinfo.name returns the interface instead of the actual class.Hm... classinfo (now typeid) should get the most derived type from an instance. This may be a factor of it being an interface instance vs. a class instance. A simple test: Stevens-MacBook-Pro:~ steves$ cat testinterface.d import std.stdio; interface I { } class C : I { } void main() { I i = new C; writeln(typeid(i).name); writeln(typeid(cast(Object)i).name); } Stevens-MacBook-Pro:~ steves$ ./testinterface testinterface.I testinterface.C Looks like that is the case. Note that classinfo is not part of the language any more, and will likely be deprecated. typeid is the correct mechanism to get the TypeInfo of a derived class. I'm thinking this is incorrect, typeid should get the derived class type IMO. It shouldn't be that difficult or costly for the compiler to do.
Feb 24 2014
Fixed code with all combination examples: (again, only WindowsButton works with WindowsGui and same for LinuxGui... but the classes don't use iButton like they should!!! Oh, they break the contract!! End of the world!) http://dpaste.dzfl.pl/02ee45225303 Output: Trying WindowsButton with WindowsGui! Do(): WindowsButton WindowsButton.foo(): I'm an extra WindowsButton feature! ...WindowsButton works in WindowsGui! Trying LinuxButton with WindowsGui! Invalid object type dependency mismatch! Type: f324.LinuxButton Type Expected: WindowsButton Trying WindowsButton with LinuxGui! Invalid object type dependency mismatch! Type: f324.WindowsButton Type Expected: LinuxButton Trying LinuxButton with LinuxGui! Do(): LinuxButton ...LinuxButton works in LinuxGui!
Feb 24 2014
On Mon, 24 Feb 2014 16:24:00 -0500, Frustrated <c1514843 drdrb.com> wrote:On Monday, 24 February 2014 at 18:59:32 UTC, Steven Schveighoffer wrote:Always have seen the point, it is why I suggested this solution.On Mon, 24 Feb 2014 11:36:50 -0500, Frustrated <c1514843 drdrb.com> wrote:Thanks. Now the correct type is known. One could, for example, look for wrappers/adapters/etc to get a linuxbutton into a windowsbutton(if so desired, throw a well informed error, etc. Hopefully though, now you see the point. It is a runtime contract that you make since you can't specify it at compile time(since D doesn't supports it).http://dpaste.dzfl.pl/c25655e2dfe9 The code above simplifies using interfaces as the programming object. It allows one to program the derived classes as if they were not part of an abstraction by mapping the abstracted virtual methods to concrete methods. e.g., class WindowsGui : iGui { WindowsButton _button; property WindowsButton button(WindowsButton b) { return (_button = b); } mixin(Fixup!(WindowsGui, iButton, WindowsButton)); } instead of class WindowsGui : iGui { WindowsButton _button; property iButton button(iButton b) { assert(cast(WindowsButton)b !is null, `Invalid object type dependency mismatch! Type: `~b.classinfo.name~` Type Expected: WindowsButton`); auto bb = cast(WindowsButton)b; // do work with bb. } }Nice work!One problem with the template is that b.classinfo.name returns the interface instead of the actual class.Hm... classinfo (now typeid) should get the most derived type from an instance. This may be a factor of it being an interface instance vs. a class instance. A simple test: Stevens-MacBook-Pro:~ steves$ cat testinterface.d import std.stdio; interface I { } class C : I { } void main() { I i = new C; writeln(typeid(i).name); writeln(typeid(cast(Object)i).name); } Stevens-MacBook-Pro:~ steves$ ./testinterface testinterface.I testinterface.C Looks like that is the case. Note that classinfo is not part of the language any more, and will likely be deprecated. typeid is the correct mechanism to get the TypeInfo of a derived class. I'm thinking this is incorrect, typeid should get the derived class type IMO. It shouldn't be that difficult or costly for the compiler to do.Unfortunately the main downside is the vtable is twice as big. Final or static methods could be used for this. If the compiler could manage such a system it could do the job better. (There would be no extra functions needed and the calls would be faster)First, the vtable is not per-instance, so it doesn't detract too much from the solution. Second, the concrete function does not need to be virtual, make it final, and it won't add to the vtable.The issue is this: I want to program to interfaces(requiring the class(WindowsGui) to use base interfaces(iButton) to satisfy the interface. The problem is this compile time contract is to generic and makes coding the classes more verbose. The mixin reduces the verbosity and provides a runtime contract(we enforce it using asserts in this case). The benefit of the mixin is that coding WindowsGui is simpler and more direct and it's dependency on WindowsButton(not iButton) is known at compile time(But enforced at runtime by assert). Would you not agree? Everything else is essentially identical.I have written such libraries before (I had an OS abstraction library in C++), it's not always pretty. -Steve
Feb 24 2014