Koru 2 Posted June 21, 2020 Interface-oriented development is an awesome tool, and the ARC memory management that usually comes with it can be great as well. But in our beloved Delphi, interfaces are not full citizens. Working with Delphi interfaces becomes soon troublesome; the way they have been implemented makes impossible some of the common stuff we do in our daily programming As an example, you can't use an interface method as an event handler. You can't use anything that expects a "procedure of object" or "function of object", nor use generics or other modern features. Why? Because the interfaces we have in Delphi were introduced just and only to achieve COM integration. So, the interfaces don't know they always have an object (delphi class) behind them So we need a remake of Delphi interfaces, but we can't break the software already using them Here comes the "native interface": probably they should have a distinction on their declaration, so the compiler knows to handle them And the big difference using them, is that native interface methods are object methods, so the compiler would know how to handle them. I would like this post to serve as brainstorming for those of you who have ideas about how to enable this division of the com-based interface and a possible new native implementation, implementation of which there may also be proposals for improvement. For example, at least for me, it would be nice if interfaces (at least the native ones) could have multiple inheritance of interfaces: InterfaceTest = ninterface(Interface1, Interface2, Interface3) procedure Test; end; Feel free to comment in any direction. 2 Share this post Link to post
Guest Posted June 21, 2020 Brain storming, huh..... i have some still working. How about reverse interface declaration, i mean let the compiler build base interfaces from classes Like this type TProcessor = class public constructor Create; procedure ProcessData(var Data); IMethod; // IMethod will only be used when InterfaceOf is declared procedure CleanUp; IMethod; end; IProcessor = InterfaceOf(TProcessor); // IProcessor with the above one line declaration is identical to the following { IProcessor = interface procedure ProcessData(var Data); procedure CleanUp; end; } // and can be extended like this IPostProcessor = InterfaceOf(TProcessor) procedure NotifyFinish; end; // or like this TCompressor = class public procedure Compress; abstract; IMethod; end; IDataCompressor = Interface(InterfaceOf(TProcessor), IInterface, InterfaceOf(TCompressor)); Share this post Link to post
Uwe Raabe 2057 Posted June 21, 2020 Instead of IMethod I would suggest a new directive interfaced, optional with an alternative name used with InterfaceOf (similar to a method resolution clause). type TProcessor = class public constructor Create; procedure ProcessData(var Data); interfaced; procedure CleanUp; interfaced(Clear); // InterfaceOf will publish this method as Clear procedure Clear; // used inside TProcessor property Value: Integer read GetValue; interfaced; // should work for properties, too end; 1 Share this post Link to post
Koru 2 Posted June 21, 2020 2 hours ago, Kas Ob. said: Brain storming, huh..... i have some still working. How about reverse interface declaration, i mean let the compiler build base interfaces from classes Like this type TProcessor = class public constructor Create; procedure ProcessData(var Data); IMethod; // IMethod will only be used when InterfaceOf is declared procedure CleanUp; IMethod; end; IProcessor = InterfaceOf(TProcessor); // IProcessor with the above one line declaration is identical to the following { IProcessor = interface procedure ProcessData(var Data); procedure CleanUp; end; } // and can be extended like this IPostProcessor = InterfaceOf(TProcessor) procedure NotifyFinish; end; // or like this TCompressor = class public procedure Compress; abstract; IMethod; end; IDataCompressor = Interface(InterfaceOf(TProcessor), IInterface, InterfaceOf(TCompressor)); Hi, thank you very much for your proposals! I talk about personal tastes, but I prefer to see at a glance the definition of the interface (contract), for me it's more difficult the way you present the new interface since you have to extract it mentally from different parts of the source code Share this post Link to post
Guest Posted June 21, 2020 13 minutes ago, Koru said: I talk about personal tastes, but I prefer to see at a glance the definition of the interface (contract), for me it's more difficult the way you present the new interface since you have to extract it mentally from different parts of the source code Sure, i simply always prefer the choice itself, that might reversed declaration for me is more maintainable and easier to tweak, such change can be in one place, but as you said it is personal taste. In case such declaration been added to Delphi syntax, we can simply start nagging Uwe to add simple extract interface from class method, and viola you have your classic interface. @Uwe Raabe I wrote interfaced at first then changed it, the word interfaced made me giggle a little, for my non native English ears it sounded like bunched in the face or smacked-faced 🙂 Share this post Link to post
Koru 2 Posted June 22, 2020 Speaking about possible new features, it's a mess that the Supports function doesn't see any of the base interfaces of a possible inheriteance tree. Only the interfaces that are declared in the class header are visible to this function, so you are forced to write the interfaces in the header, as you see you need them here or there. So it would be nice that this kind of functions can work with base interfaces Share this post Link to post
Uwe Raabe 2057 Posted June 22, 2020 5 minutes ago, Koru said: Speaking about possible new features, it's a mess that the Supports function doesn't see any of the base interfaces of a possible inheriteance tree. Only the interfaces that are declared in the class header are visible to this function, so you are forced to write the interfaces in the header, as you see you need them here or there. So it would be nice that this kind of functions can work with base interfaces I am not sure if I can support this request. It is a simplification to avoid writing code, but it should be well thought before implementing it. There might be uses cases when this is unwanted and when it is done this way you may have a hard time to get rid of it again. Perhaps I can think about a use case for the current state. Share this post Link to post
Anders Melander 1782 Posted June 22, 2020 2 hours ago, Koru said: ... it's a mess that the Supports function doesn't see any of the base interfaces of a possible inheriteance tree. Only the interfaces that are declared in the class header are visible to this function ... I think I have encountered this problem on rare occasions but I can't remember the circumstances. Did you mean interface inheritance? For example the following works fine: type IFoo = interface ['{F99BAE4A-6612-4714-96B4-237763239C7F}'] end; IBar = interface ['{570FBD40-8ECF-4B4B-9898-EF3F4146FFF9}'] end; type TFoo = class(TInterfacedObject, IFoo) end; TBar = class(TFoo, IBar) end; type TFooObject = class(TComponent, IFoo) end; TBarObject = class(TFooObject, IBar) end; procedure Test; begin // Supports(interface) var FooBar := TBar.Create as IUnknown; Assert(Supports(FooBar, IFoo)); Assert(Supports(FooBar, IBar)); // Supports(instance) var FooBarObject := TBarObject.Create(nil); Assert(Supports(FooBarObject, IFoo)); Assert(Supports(FooBarObject, IBar)); end; 1 Share this post Link to post
Uwe Raabe 2057 Posted June 22, 2020 (edited) If I am not mistaken, Koru is referring to a different case: type IBar = interface ['{570FBD40-8ECF-4B4B-9898-EF3F4146FFF9}'] end; IFooBar = interface(IBar) ['{1A99C9D3-CC94-4BCE-BF9C-354BF14EC5C7}'] end; type TFooBar = class(TInterfacedObject, IFooBar) end; procedure Test; begin var FooBarObject := TFooBar.Create; Assert(Supports(FooBarObject, IFooBar)); Assert(Supports(FooBarObject, IBar)); //Fails! end; For me this is pretty logical, so nothing to be changed. Edited June 22, 2020 by Uwe Raabe 3 Share this post Link to post
Anders Melander 1782 Posted June 22, 2020 1 hour ago, Uwe Raabe said: For me this is pretty logical, so nothing to be changed. I agree. Interface inheritance is just syntactic sugar. http://edn.embarcadero.com/article/29779 (warning this links contain references to The Version That Must Not Be Mentioned: Delphi 8) 1 Share this post Link to post
Koru 2 Posted June 22, 2020 12 minutes ago, Uwe Raabe said: If I am not mistaken, Koru is referring to a different case: type IBar = interface ['{570FBD40-8ECF-4B4B-9898-EF3F4146FFF9}'] end; IFooBar = interface(IBar) ['{1A99C9D3-CC94-4BCE-BF9C-354BF14EC5C7}'] end; type TFooBar = class(TInterfacedObject, IFooBar) end; procedure Test; begin var FooBarObject := TFooBar.Create; Assert(Supports(FooBarObject, IFooBar)); Assert(Supports(FooBarObject, IBar)); //Fails! end; For me this is pretty logical, so nothing to be changed. Hi, yes I was referring to something like this case you point. Well, it may be that the approach was not very correct. My idea focused on a context where we can ask native interfaces for information similar to what we can ask to classes, such as for example: if it inherits from a certain base interface. And in that context I was handling the idea (perhaps wrong, we are talking about ideas and giving possibilities) that the Supports function was capable of giving that functionality Share this post Link to post
Koru 2 Posted June 22, 2020 3 hours ago, Uwe Raabe said: If I am not mistaken, Koru is referring to a different case: type IBar = interface ['{570FBD40-8ECF-4B4B-9898-EF3F4146FFF9}'] end; IFooBar = interface(IBar) ['{1A99C9D3-CC94-4BCE-BF9C-354BF14EC5C7}'] end; type TFooBar = class(TInterfacedObject, IFooBar) end; procedure Test; begin var FooBarObject := TFooBar.Create; Assert(Supports(FooBarObject, IFooBar)); Assert(Supports(FooBarObject, IBar)); //Fails! end; For me this is pretty logical, so nothing to be changed. Hi again, why do you see it logical? I think it seems quite illogical to ask that to this function and to answer that it's false when we know it's true Share this post Link to post
Stefan Glienke 2002 Posted June 22, 2020 There is nothing logical about it - as the previously linked article says: Quote This requirement is an historical artifact of some OLE2 bugs with IClassFactory and IClassFactory2, and some other interfaces. 1 Share this post Link to post
Koru 2 Posted June 22, 2020 12 minutes ago, Stefan Glienke said: 17 minutes ago, Stefan Glienke said: There is nothing logical about it - as the previously linked article says: Hi, not having an extensive knowledge of the subject can lead me to erroneous statements, if so, do not hesitate to correct me. If we talk about a new implementation of the interfaces as native interfaces, no longer tied to COM, I understand that Embarcadero can make those limitations cease to exist, is what I say correct or is there something I am leaving out? Share this post Link to post
Anders Melander 1782 Posted June 22, 2020 29 minutes ago, Koru said: I think it seems quite illogical to ask that to this function and to answer that it's false when we know it's true But it isn't true. The IFooBar interface inherits the methods of IBar - at compile time. The implementing object doesn't inherit the contract that it must implement the IBar interface. 17 minutes ago, Stefan Glienke said: as the previously linked article says I'm not convinced that John Kaster knew what he was talking about when he wrote that. I mostly included the link because it seems to be the earliest mention of the feature. The limitation as-is matches my recollection of "the rules of COM" but I could be mistaken. I'm not up to reading the COM bible again just to find out. It was hard enough the first time(s). 1 Share this post Link to post
Stefan Glienke 2002 Posted June 22, 2020 (edited) 4 minutes ago, Anders Melander said: But it isn't true. The IFooBar interface inherits the methods of IBar - at compile time. The implementing object doesn't inherit the contract that it must implement the IBar interface. According to that logic IFooBar must not be assignment compatible to IBar - but it is. Even the compiler knows about this because actually if you implement IFooBar and IBar into the same class without any specific method resolution clauses it puts both IMTs into the very same slot (IBar shares it with the IFooBar one) Edited June 22, 2020 by Stefan Glienke 2 Share this post Link to post
Koru 2 Posted June 22, 2020 6 minutes ago, Anders Melander said: But it isn't true. The IFooBar interface inherits the methods of IBar - at compile time. The implementing object doesn't inherit the contract that it must implement the IBar interface. I'm not convinced that John Kaster knew what he was talking about when he wrote that. I mostly included the link because it seems to be the earliest mention of the feature. The limitation as-is matches my recollection of "the rules of COM" but I could be mistaken. I'm not up to reading the COM bible again just to find out. It was hard enough the first time(s). I have the feeling that we are talking about how it works now (you correct me if I'm wrong), not how we would like it to work, and I talk about how I would like it to work. In this specific case if the native interfaces have inheritance capacity, these possibilities would be viable, if they are clearly useful for the developers of course Share this post Link to post
Stefan Glienke 2002 Posted June 22, 2020 "Native Interface" is a terrible name tbh - what exactly would be "native" about it that current interfaces are not. A look at other languages might be helpful to actually define clearly what your desired requirements are - for example traits in Scala. 4 Share this post Link to post
Koru 2 Posted June 22, 2020 8 minutes ago, Stefan Glienke said: "Native Interface" is a terrible name tbh - what exactly would be "native" about it that current interfaces are not. A look at other languages might be helpful to actually define clearly what your desired requirements are - for example traits in Scala. Putting proper names always leads to headaches And it is relevant, but it is not the goal of the post. The goal is to know what deficiencies or limitations we see to the current interfaces in Delphi, partly (or mostly) because they were born from COM. By native interface we have been calling in the post to that interface that we know that behind it has a Delphi class, and under that assumption I would like things like the ones I list in the first post, that probably would be enough advance for me: 'As an example, you can't use an interface method as an event handler. You can't use anything that expects a "procedure of object" or "function of object", nor use generics or other modern features' But I know that there are people like you who that have struggled a lot with the interfaces and I'm sure you have clearer limitations that perhaps I can't see. Share this post Link to post
Javier Tarí 23 Posted June 22, 2020 38 minutes ago, Stefan Glienke said: "Native Interface" is a terrible name tbh - what exactly would be "native" about it that current interfaces are not. It's just a name, any name would do. This starting name aludes to the fact that such interface would be implemented by Delphi native classes, and that means a lot, as this way the compiler does not just know a method address, but the object instance. And that gives us full compatibility with anything currently implemented that works with class methods. On the other side, there is already an embarcadero answer on Quality portal, acknowledging this is feasable, and would work as expected My knowledge on interface-kind features on other languages is zero, so I wonder if there is something really useful that could improve the kind of interfaces we're suggesting, without making it overly difficult to implement and derailing this train 1 Share this post Link to post
Stefan Glienke 2002 Posted June 22, 2020 (edited) What you describe is exactly what traits are in Scala. Well minus the fact that traits can even declare fields but when they always implemented by objects that would work as well - hence I would not call them anything like "so and so interface" - because while they might be similar they would be a completely different beast and not to be mixed with them. Anyhow it would come down to no proper way to cleanup those things - Scala and all other languages that have similar things use GC so you will not have any headaches about when something has to be destroyed. If you pass around your objects as traits/ninterface/whatever with the memory management we currently have in Delphi this will just an even huger pain than you already have if you mix object and interface references to classes that either implement reference counting or not. P.S. If you are interested how Scala implements traits: https://www.nurkiewicz.com/2013/04/scala-traits-implementation-and.html Edited June 22, 2020 by Stefan Glienke 1 Share this post Link to post
Anders Melander 1782 Posted June 22, 2020 2 hours ago, Stefan Glienke said: According to that logic IFooBar must not be assignment compatible to IBar - but it is. You are correct: type IBar = interface ['{570FBD40-8ECF-4B4B-9898-EF3F4146FFF9}'] end; IFooBar = interface(IBar) ['{F99BAE4A-6612-4714-96B4-237763239C7F}'] end; type TFooBar = class(TInterfacedObject, IFooBar) end; procedure test; begin var FooBar: IFooBar := TFooBar.Create; var Bar: IBar := FooBar; end; ...but as we already know... var FooBar: IFooBar := TFooBar.Create; Assert(Supports(FooBar, IBar)); ...will fail, so there's a bug somewhere - or at least a really bad inconsistency. Supports just does a IUnknown.QueryInterface which ends up in TObject.GetInterface. The assignment on the other hand just copies the pointer and calls _AddRef. From my understanding on what interface inheritance is in Delphi, Supports() is correct and the compiler is wrong but there are obviously different interpretations of this. 1 Share this post Link to post
Stefan Glienke 2002 Posted June 22, 2020 5 minutes ago, Anders Melander said: there's a bug somewhere Yes! In the COM implementation and it was carried over to Delphi as a feature to make it work. 1 Share this post Link to post
Mahdi Safsafi 225 Posted June 22, 2020 39 minutes ago, Stefan Glienke said: What you describe is exactly what traits are in Scala. Well minus the fact that traits can even declare fields but when they always implemented by objects that would work as well - hence I would not call them anything like "so and so interface" - because while they might be similar they would be a completely different beast and not to be mixed with them. I'm not aware about Scala but is this like what java does with default implementation for interface ? 1 Share this post Link to post
Javier Tarí 23 Posted June 22, 2020 42 minutes ago, Stefan Glienke said: Yes! In the COM implementation and it was carried over to Delphi as a feature to make it work. And that's why in a COM-free world we need more capable interfaces: Developed for Delphi With true and working inheritance (or call it extension, if you wish) Always implemented by a Delphi Class and aware of it With methods that can be used anywhere a class method can be used I don't know if anything else is needed Share this post Link to post