www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Programming to Interfaces simplification

reply "Frustrated" <c1514843 drdrb.com> writes:
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
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
parent reply "Frustrated" <c1514843 drdrb.com> writes:
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:

 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.
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.
Feb 24 2014
next sibling parent "Frustrated" <c1514843 drdrb.com> writes:
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
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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:
 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.
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).
Always have seen the point, it is why I suggested this solution.
 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