Jacek Laskowski 57 Posted April 17, 2020 I got a Chad Z. Hower article: https://www.codeproject.com/Articles/1252175/Fixing-Delphis-Interface-Limitations The author is very critical about the interfaces in Delphi. But he gives examples where he mixes interface and object access, which is doomed to failure in advance. I use interfaces very much, and I don't agree with the statement that there is more trouble with them than good. I don't know e.g. C++ and I don't know how interfaces are solved there, probably my ignorance limits my understanding of the problem. Can someone enlighten me if the interfaces in Delphi are really that badly done? Share this post Link to post
Sherlock 663 Posted April 17, 2020 I don't think they are badly done. The are just stuck in time... Back in the day - you know, when COM was invented - interfaces where the go-to thing to get COM up and running in a Delphi project. Together with reference counting and GUIDs and stuff. Plus they allow for multiple inheritance. So, all ended there. Interfaces are feature complete... at least from a 90s POV. In comes a person used to a 00s language such as C# and there you have it: "I need this", "I don't know why I have to do that" etc. Programmer millenials... Share this post Link to post
David Heffernan 2345 Posted April 17, 2020 That post is worthless. Best to ignore it. 4 Share this post Link to post
Dalija Prasnikar 1396 Posted April 17, 2020 Yes, interfaces in Delphi are troublesome. From perspective of being tool for achieving abstractions they are commonly more trouble then they are worth. Chad explains some issues well. If they are used as COM interoperability, then there is nothing wrong with the because that was exactly their original purpose. If you are looking on them as tool that can give you automatic memory management they are great, because you can more easily solve some otherwise complex problems that would require complex solutions. However, their use in such cases is limited and you cannot use them for such purpose everywhere. For instance you cannot add ARC to visual frameworks that are build on top of TComponent ownership model and automatically handle lifetime of your visual controls. Main problem with interfaces is not that they are badly done per-se, but that they add duality to Delphi memory management and as such additional complexity and care is needed when you are using them. That is one of the reasons why I liked mobile ARC compiler. It showed that without that duality interfaces and objects can live happily ever after and that you don't have to look around your shoulder every time you use interfaces, expecting that they will kick you in behind. Of course, full ARC brought other issues, but those were related to using existing frameworks that were not written with ARC in mind, so we got that dreadful DisposeOf... No matter how you look at it, Delphi memory management is complex, sometimes too much... it is not question of being able to learn about things, but about how easy and how fast can you write your code, focusing on task at hand without needing to fiddle with all nuisances of handling your objects lifetime. 1 Share this post Link to post
Fr0sT.Brutal 900 Posted April 17, 2020 (edited) Delphi interfaces mix two concepts - ref-counting with auto disposal and the very interfaces as a required method set - into one type whereas frequently only one of them is needed. While ARC was bad idea because it broke too much old code, it had some sense. I guess if Delphi introduced new types like IPlainInterface and TRefCountedObject, it would be pretty nice and useful Edited April 17, 2020 by Fr0sT.Brutal Share this post Link to post
Alexander Elagin 143 Posted April 17, 2020 1 hour ago, Fr0sT.Brutal said: ... the very interfaces as a required method set ... You mean something like CORBA interfaces as implemented as FreePascal? They are exactly this: a set of methods, not refcounted. Share this post Link to post
A.M. Hoornweg 144 Posted April 17, 2020 I use interfaces all the time, and there are simple ways to make existing classes support interfaces with or without reference counting, without having to derive from a common base class like tInterfacedObject. If I want to modify any class in such a way that it supports interfaces without reference counting, I simply add dummy methods _AddRef, _Release, and QueryInterface: Type tNewObject = class(TOldObject, IInterface) protected // IInterface function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; function tNewObject._AddRef: Integer; begin Result := -1; end; function tNewObject._Release: Integer; begin Result := -1; end; function tNewObject.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE; end; And similarly, it is possible to make any existing class support interfaces with reference counting, it just takes a few more methods: TNewObject = class(TOldObject, IInterface) protected FRefCount: Integer; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; public procedure AfterConstruction; override; procedure BeforeDestruction; override; class function NewInstance: TObject; override; property RefCount: Integer read FRefCount; end; procedure TNewObject.AfterConstruction; begin // Release the constructor's implicit refcount InterlockedDecrement(FRefCount); Inherited; end; procedure TNewObject.BeforeDestruction; begin if RefCount <> 0 then Error(reInvalidPtr); Inherited; end; // Set an implicit refcount so that refcounting // during construction won't destroy the object. class function TNewObject.NewInstance: TObject; begin Result := inherited NewInstance; TNewObject(Result).FRefCount := 1; end; function TNewObject.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE; end; function TNewObject._AddRef: Integer; begin Result := InterlockedIncrement(FRefCount); end; function TNewObject._Release: Integer; begin Result := InterlockedDecrement(FRefCount); if Result = 0 then Destroy; end; 2 Share this post Link to post
Jacek Laskowski 57 Posted April 17, 2020 41 minutes ago, A.M. Hoornweg said: And similarly, it is possible to make any existing class support interfaces with reference counting, it just takes a few more methods: But try this "trick" on TComponent or it's descendants... no way. Share this post Link to post
A.M. Hoornweg 144 Posted April 17, 2020 14 minutes ago, Jacek Laskowski said: But try this "trick" on TComponent or it's descendants... no way. You shouldn't, because the component's owner manages its lifetime. Refcounted objects do not have an owner, they manage their own lifetime. Freeing an object twice is a really bad idea. 1 Share this post Link to post
Dalija Prasnikar 1396 Posted April 17, 2020 1 hour ago, Jacek Laskowski said: But try this "trick" on TComponent or it's descendants... no way. Which is exactly one of the issues mentioned in article. Interface should be an abstraction that is always handled the same way, but implementation details of the particular class will leak and in some cases you will need to know how implementing class handles memory management. And then whole abstraction castle will just collapse. Share this post Link to post
Sherlock 663 Posted April 17, 2020 Oh, come on. That is not really an issue, it's just a No-No. Just like not changing some Label on a form from a thread without synchronizing. Just don't do it, and you will not get into trouble. Share this post Link to post
Bill Meyer 337 Posted April 17, 2020 I use interfaces and find them very effective -- in their place. But I curse the unimaginative implementation, which seems unlikely ever to be improved. I understand that they came about to support COM. But I rarely make use of them in that context. Instead, they have become a tool for memory management, and for limiting exposure of class internals. That said, it would be very nice to be able to make getters and setters in interfaces private. That you cannot do so makes the use of properties in interfaces almost silly. But I would prefer to expose the properties, and hide the underlying methods. There has been much change since D3 in the ways most of us design our code, but the interface is like a fly captured in amber. Share this post Link to post
Dalija Prasnikar 1396 Posted April 17, 2020 32 minutes ago, Sherlock said: Oh, come on. That is not really an issue, it's just a No-No. Just like not changing some Label on a form from a thread without synchronizing. Just don't do it, and you will not get into trouble. Yes, it is an issue. Even if you ignore "just don't do it" part which you first have to know exists and then have to learn how to do properly in Delphi (not so easy as in Java, C# or other languages where you can use interfaces easily), main problem is that you cannot just have some IFoo interface and implement it on every class you like and use it equally. As long as all implementing classes handle ARC the same way, it is fine, but try having IFoo on TInterfacedObject descendant and TComponent descendant and you are basically screwed (or at least seriously limited in what you can do with such classes). Share this post Link to post
Rollo62 536 Posted April 17, 2020 Would be maybe a good time and place to make some clear proposals howto improve interfaces, instead of moaning all the time. So how exactly should they get fixed, so that all can be happy ? Share this post Link to post
David Heffernan 2345 Posted April 17, 2020 4 minutes ago, Dalija Prasnikar said: Even if you ignore "just don't do it" part which you first have to know exists and then have to learn how to do properly in Delphi Is it unreasonable to expect that programmers have knowledge and skill? 5 Share this post Link to post
Dalija Prasnikar 1396 Posted April 17, 2020 19 minutes ago, David Heffernan said: Is it unreasonable to expect that programmers have knowledge and skill? No,, but learning takes time. I am not talking here about lazy people that don't want to learn, I am talking about efficiency of learning a language that has additional levels of complexity comparing to learning language that doesn't and where both languages are generally suitable for solving some problem. I know that knowledge accumulates over time, so you will not have to relearn things you know the next time around, but such things do have overall impact on productivity. 1 Share this post Link to post
Dalija Prasnikar 1396 Posted April 17, 2020 1 hour ago, Rollo62 said: Would be maybe a good time and place to make some clear proposals howto improve interfaces, instead of moaning all the time. So how exactly should they get fixed, so that all can be happy ? Full ARC compiler on all platforms and getting rid of TComponent ownership model would be the only proper solution. But that would definitely not make everyone happy. Maybe additional "interfaces" as abstraction that is not tied with memory management could solve most of the issues, but I never given that too much thought in terms of finding potential gotchas and additional levels of complexity such feature might add. Share this post Link to post
Fr0sT.Brutal 900 Posted April 17, 2020 4 hours ago, Alexander Elagin said: You mean something like CORBA interfaces as implemented as FreePascal? They are exactly this: a set of methods, not refcounted. Yeah, they got it right. Though they have to keep Delphi compat. Could both interface types be used together? 4 hours ago, A.M. Hoornweg said: I use interfaces all the time, and there are simple ways to make existing classes support interfaces with or without reference counting, without having to derive from a common base class like tInterfacedObject. If I want to modify any class in such a way that it supports interfaces without reference counting, I simply add dummy methods _AddRef, _Release, and QueryInterface: Very nice workaround! Share this post Link to post
Alexander Elagin 143 Posted April 17, 2020 2 minutes ago, Fr0sT.Brutal said: Yeah, they got it right. Though they have to keep Delphi compat. Could both interface types be used together? I guess yes (the {$INTERFACES} directive is local) but honestly I have not tried it myself because it is incompatible with Delphi code Share this post Link to post
Rollo62 536 Posted April 17, 2020 28 minutes ago, Dalija Prasnikar said: Full ARC compiler on all platforms and getting rid of TComponent ownership model would be the only proper solution. But that would definitely not make everyone happy. Maybe additional "interfaces" as abstraction that is not tied with memory management could solve most of the issues, but I never given that too much thought in terms of finding potential gotchas and additional levels of complexity such feature might add. Yes, Rx10.4 will move in the opposite direction then. Maybe someone else has some practical thoughts too, how to get most of it. I personally can live with the interface as is well, but of coarse the pitfalls are deep. How about a better error-detection by the compiler/linker, would that be thinkable ? Couldn't be all the do's and dont's in the compiler logic (as switchable error option of coarse, for those who need it as is) ? Probably that not possible either, but maybe the most common, drastic mistakes/misuses could be turned into some clear compile time errors (warnings). Share this post Link to post
Jacek Laskowski 57 Posted April 20, 2020 On 4/17/2020 at 1:31 PM, A.M. Hoornweg said: You shouldn't, because the component's owner manages its lifetime. Refcounted objects do not have an owner, they manage their own lifetime. Freeing an object twice is a really bad idea. Very common, basically always, used database classes like TDataset, etc. I create in runtime, they are for example part of business classes, as "Owner" I give them nil. There I would like to use them in the interface model, not in the VCL management model. Share this post Link to post
Fr0sT.Brutal 900 Posted April 20, 2020 There are plenty of object wrappers that allow using ref-counting with any object, and everyone could write his own one in 10 minutes (I did, and use it for temp datasets) 1 Share this post Link to post
Dalija Prasnikar 1396 Posted April 20, 2020 On 4/17/2020 at 5:24 PM, Rollo62 said: How about a better error-detection by the compiler/linker, would that be thinkable ? Couldn't be all the do's and dont's in the compiler logic (as switchable error option of coarse, for those who need it as is) ? Probably that not possible either, but maybe the most common, drastic mistakes/misuses could be turned into some clear compile time errors (warnings). There is nothing compiler can do here. Compiler does not have intrinsic knowledge how particular class implements reference counting and therefore cannot emit warnings in proper places because it does not know whether particular piece of code is correct or not. Share this post Link to post
Rollo62 536 Posted April 20, 2020 (edited) @Dalija Prasnikar Yes, I think compiler/linker could get a little smarter though, maybe in the direction like FixInsight or PascalAnalyser can do. To find common anti-patterns, and give warnings, that should be not so far out of reach. But you're right, maybe this is a task for some separate tool probably. Edited April 20, 2020 by Rollo62 2 Share this post Link to post