Peter Gerwinski wrote:
What are these "interfaces"? (Somebody already explained it to me some time ago, but I have forgotten ... #-)
Ok, here are some explanations from "Java in a Nutshell", with some comments by me, and some ideas how to implement them in gpc:
"[Text describes a situation that seems to need MI] \footnote{ C++ allows classes to have more than one superclass, using a technique known as 'multiple inheritance'. Multiple inheritance opens up a can of worms, so Java replaces it with what many believe is a more elegant solution. }
Java's solution to this problem is called an interface. An interface looks a lot like an abstract class, except that it uses the keyword 'interface' instead of the words 'abstract' and 'class'. [...]
While an 'abstract' class may define some 'abstract' methods and some non- 'abstract' methods, all the methods defined within an interface are implicitly 'abstract'. [The 'abstract' keyword can, but doesn't have to, be omitted.] [Note: In Java, all methods are what we call virtual.] A further restriction on interfaces is that any variables declared in an interface must be 'static' and 'final' - that is, they must be constants [ordinary constants, not initialized variables, nor object constants - since ordinary constants don't appear in BP's object type declarations at all, this restriction effectively means there can't be any variables or constants in an interface in gpc, so an interface consists only of declarations of abstract virtual methods (**, see below)].
So what can we do with an interface? Just as a class 'extends' [is derived from] its superclass, it also optionally 'implements' an interface. 'implements' s a Java keyword that can appear in a class declaration following the 'extends' clause. 'implements' should be followed by the name of the interface that the class implements. In order to implement an interface, a class must first delcare the interface in an 'implements' clause, and then it must provide an implementation (i.e. body) for all of the 'abstract' methods of the interface. \footnote{ This is the real difference between multiple inheritance in C++ and interfaces in Java. In C++, a class can interit method implementations from more than one superclass. In Java, a class can inherit actual implementations only from one superclass. It can inherit additional 'abstract' methods [declarations] from interfaces, but it must provide its own implementation of those methods. It is rare, however, to actually be able to use C++ multiple inheritance to inherit useful, non-trivial implementations from more than one class. The elegance and simplicity of Java's interface more than compensate for the inability to inherit implementations from more than one class. } [...]
Using Interfaces
[...] [Syntax translated to Pascal, tentatively:] Type Drawable=Interface [this wouldn't introduce a new reserved identifer, but I'm not sure if it can be parsed easily.] Procedure Draw(dw:DrawWindow); ['virtual;' or 'abstract;' optionally] [possibly other methods] End;
[declaration of type Rectangle that doesn't have a Draw method]
DrawableRectangle=Object(Rectangle;Drawable) [in Java this is: public class DrawableRectangle extends Rectangle implements Drawable] x,y:Double; Constructor Init(w,h:Double); Procedure Draw(dw:DrawWindow); virtual; [other methods, destructor] End;
[implementation of all methods, constructor and destructor]
Var d:^Drawable; r:^Rectangle; dr:^DrawableRectangle;
Begin New(dr,Init(2.3,4.5)); d:=dr; r:=dr; dr.Draw; d.Draw; End. [...]
[Classes can implement several interfaces ('implements Drawable, Scalable').] [...]
Extending Interfaces [...] public interface Transformable extends Scalable, Rotateable, Reflectable public interface DrawingObject extends Drawbable, Transformable public class Shape implements DrawingObject [So there is 'multiple inheritance' between interfaces - which makes no problems!]
[...] No 'implements' clause is permitted in an interface declaration.
An interface body may not contain any constructors, static class initializers, instance variables, or class methods. [...]"
Concerning a possible implementation in gpc:
AFAICS, the only thing that really makes problems are variables (or parameters) of interface types.
First of all, since interfaces can't be instantiated, such variables or parameters must be pointers (or var parameters). In Java, this is implied, since *all* object variables are pointers.
But such a pointer must point to the actual object and to the methods declared in the interface. Since different object types can implement the same interface, those methods will not always be at the same VMT offset. However, it is possible to guarantee that all methods of one interface are at consecutive VMT offsets (in the order they're declared in the interface). (*, see below)
So one needs the VMT offset of the interface's first method.
I can see (at least) two solutions:
- A "pointer" to an interface variable consists of two parts: the actual pointer to the variable, and the VMT offset of the first method (or, alternatively, directly the adress of the first method in the VMT).
Disadvantage: The pointer gets twice as big. The difference must be considered when assigning it to another pointer (this could be an untyped pointer or a pointer of one of the "parent" interfaces - in the latter case the VMT offset has to be adjusted).
- The VMT must contain information about all interfaces that are implemented together with the addresses of their methods. However, since different object types can implement different interfaces, and one type can implement more than one interface, I can't think of a method that doesn't involve some kind of searching (searching the wanted interface out of possibly many interfaces).
So this is basically a speed vs. size tradeoff. For most applications, I think, speed is to be favoured (and 32 bit programming favours speed, anyway), so I'd prefer the first solution. I can't see a solution without a need of either searching or additional data *per variable* (whereas additional data per VMT wouldn't matter so much).
(*) If we allow the following inheritance between interfaces A / \ B C \ / D (and I don't see a reason why not), the methods of A will have to be duplicated in D, so that it's possible to "cast" a ^D into a ^B or a ^C. So the "VMT" of D would look like
Methods of A _ "B" New methods of B / Methods of A _ "C" New methods of C /
The two parts "Methods of A" would contain the same information, but since this is only in the VMTs, I don't think it wastes very much memory.
I hope I was clear in most things. If not, please ask again. Of course, there will be some "details" left to be solved, but I hope, no major problems.
(**) This last thing is a bit off-topic to interfaces, so I put it at the end. Thinking about object constants again, I think there can be "un-virtual" constants, i.e. constants that can be redeclared in child classes, but the parent class will always use their own constants. So, effectively, they're oridinary constants, with the only difference that they're visibility is limited to the object type (applying the usual rules of public/protected/private). These constants don't have to be typed, of course. Also, one can imagine "un-virtual" class variables, very similarly. They should be relatively easy to implement (since the only difference to ordinary constants/variables is the visibility). If this is done, interfaces could contain constants, like in Java.