Interfaces

BlitzMax Forums/BlitzMax Programming/Interfaces

Yasha(Posted 2013) [#1]
Just for fun... I've seen a few threads talk about this but nobody seems to have actually given it a go, so here is my take:


Interface.bmx:


interface_allocprotect.c:



And an example:

Import "Interface.bmx"
SuperStrict

Type Foo
	Field s:String
	Method act()
		Print "This Foo is fooping!"
	End Method
	Method show()
		Print "s is " + s
	End Method
	Method ToString:String()
		Return "Foo!"
	End Method
End Type

Type Bar
	Field x:Int
	Method show()
		Print "x is " + x
	End Method
	Method act()
		Print "This Bar is barping with " + x
	End Method
End Type


Type IFooBar Extends TInterface
	Method show() Abstract
	Method act() Abstract
End Type


Local f:Foo = New Foo
f.s = "FortyTwo"
Local g:Foo = New Foo
g.s = "SixtyFour"
Local b:Bar = New Bar
b.x = 42

Local i:IFooBar = IFooBar(TInterface.Interface(f, "IFooBar"))
Local j:IFooBar = IFooBar(TInterface.Interface(b, "IFooBar"))
Local k:IFooBar = IFooBar(TInterface.Interface(g, "IFooBar"))

'Foo and Bar objects now run together despite *no* inheritance links! Yay!
i.show
j.show
k.show
i.act
j.act
k.act



How it works:

TInterface.Interface abuses reflection to forcibly instantiate the abstract class being used as an interface definition. It then hunts through the implementors searching for matching methods (complaining if it doesn't find them), and replaces the contents of the interface object's vtable with JITted trampolines to forward all method calls through to the host object untouched. New vtables are created as necessary to reflect whatever is in the implementor, so in the above example i and j don't share a vtable despite both having been spawned from IFooBar.

An interface should consist only of a list of abstract method declarations. Field accesses cannot be forwarded to the base object, so they will get ignored, and any non-abstract method bodies will just get overwritten (all runnable code should go in class types, as normal).

It is permitted to inherit declarations from another interface, but this will likely serve very little purpose in practice.

Seems to work OK. Performance should be good; the trampolines are only 5 machine instructions long. Since it's effectively untested I am presenting it as a "needs work" solution though with the assumption that there are errors left to find.


Limitations:

-- The interface won't show as identical to the original object when compared with = (although it does forward the Compare method, so sorting containers should hopefully work as though it were the original). This is because it's technically been wrapped rather than cast
-- Similarly, "casting" to the interface a second time will yield a new object identity again (but again... same Compare)
-- Basically, don't use = with these

-- x86 only. Updating it for PPC should be easy for anyone who knows PPC assembly (and who thinks OSX 10.5 still matters)

-- Reflection doesn't work; it should be possible to make it work but would require a lot of runtime-fu. Anyway the original object is exposed as a "base" field so you can just reflect on that, which makes more sense since interfaces aren't supposed to be "things"
-- Cannot put a second Interface layer over an existing interface object (this is a. not something that seems sensible, and b. can be solved in the same way as the above)

-- Not even remotely tested properly. Heck, I can't even be bothered to check that the *nix code in the C file is correct
-- Hardcoded pointer offsets mean this is pretty much guaranteed to break if a BlitzMax update changes the object structure
-- Limitations of BlitzMax reflection prevent method type signatures from being checked properly, and mean that this won't work with "interesting" signatures (pointers, functions, etc.)

-- If you don't know what interfaces are... go and use Monkey or Java or something that has them for realsies, please don't try to learn with this


The above files are public domain. I accept no responsibility for any problems caused as a result of actually using this code.


ziggy(Posted 2013) [#2]
That's very nice! It would be great to have this on BlitzMax with a nice Interface / End Interface syntax


degac(Posted 2014) [#3]
It seems incredible...but at the end I understood what are Interfaces and in what they are useful (thank to your example :) even if you said dont' try to learn with this!

(a little bit complex syntax... but I got the idea)
Thanks!


Brucey(Posted 2015) [#4]
... it's all a bit scary and hacky, but very clever nonetheless, Yasha!

Here's my take on interfaces with an example based on example 2 from here : http://www.wideskills.com/java-tutorial/java-abstract-class-and-interface



and the output, which matches nicely against the example output on the other site :
I am in methodI1 of class B1
I am in methodI2 of class B1
I am in methodI1 of class B1
I am in methodI2 of class B1
var2 : I am in methodC1 of class A1
var3 : I am in methodC1 of class A1
var4 : toString() method of class A1
var5 : toString() method of class A1
var6 : 0x9082fe0
I am in methodI1 of class B1
I am in methodI1 of class B1
I am in methodI1 of class B1


Not quite ready for "prime time" yet, but it's ticking a lot of the boxes so far :o)