Jump to content
Koru

Should Delphi have native interfaces?

Recommended Posts

My thoughts:

2. modify QueryInterface to take care of looking through the implemented interfaces if any of them inherits from the requested if it was not implemented implicitly - or simply add base interfaces to the implementing class - not a big deal fwiw.

3. what exactly does that mean?

4. reference counting issue - a procedure of object is just two raw pointers - how does that mix with interfaces - now if those COM-free interfaces would not have reference counting implemented the problem I described previously arises which will make them rather pointless.

Edited by Stefan Glienke
  • Like 1

Share this post


Link to post
58 minutes ago, Stefan Glienke said:

My thoughts:

2. modify QueryInterface to take care of looking through the implemented interfaces if any of them inherits from the requested if it was not implemented implicitly - or simply add base interfaces to the implementing class - not a big deal fwiw.

3. what exactly does that mean?

4. reference counting issue - a procedure of object is just two raw pointers - how does that mix with interfaces - now if those COM-free interfaces would not have reference counting implemented the problem I described previously arises which will make them rather pointless.

The usage of the native interfaces must be more or less the same as the com based, I mean the declaration should be more or less equal maybe with some new reserved word, and the implementation in a class should be similar also.

A silly example could be:

 IMyTest= nativeinterface
   ['{16E3EB88-4654-495A-9699-AC9FA2612352}']
   procedure DoProc;
   function DoFunc: Boolean;
 end;

 TMyTest = class(TNativeInterfacedObject, IMyTest)
 public
   procedure DoProc;
   function DoFunc: Boolean;
 end;

With actual interfaces you can't do next (Button2Click will fail at compile time):

type
 TProcTest = procedure of object;
 TFuncTest = function : Boolean of object;

 IMyTest= interface
   ['{16E3EB88-4654-495A-9699-AC9FA2612352}']
   procedure DoProc;
   function DoFunc: Boolean;
 end;

 TMyTest = class(TInterfacedObject, IMyTest)
 public
   procedure DoProc;
   function DoFunc: Boolean;
 end;

 TExecutor = class
   public
     procedure ExecuteIfCheckIsTrue(const AExec: TProcTest; const ACheck: TFuncTest);
 end;

  TForm67 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form67: TForm67;

implementation

{$R *.dfm}

{ TMyTest }

function TMyTest.DoFunc: Boolean;
begin
  Result := True;
end;

procedure TMyTest.DoProc;
begin
  ShowMessage('Hi!');
end;

{ TExecutor }

procedure TExecutor.ExecuteIfCheckIsTrue(const AExec: TProcTest;
  const ACheck: TFuncTest);
begin
  if ACheck() then
    AExec();
end;

procedure TForm67.Button1Click(Sender: TObject);
var
  L1: TMyTest;
  L2: TExecutor;
begin
  L1 := TMyTest.Create;
  L2 := TExecutor.Create;
  L2.ExecuteIfCheckIsTrue(L1.DoProc, L1.DoFunc);
end;

procedure TForm67.Button2Click(Sender: TObject);
var
  L1: IMyTest;
  L2: TExecutor;
begin
  L1 := TMyTest.Create;
  L2 := TExecutor.Create;
  L2.ExecuteIfCheckIsTrue(L1.DoProc, L1.DoFunc);  //this statement doesn't compile
end;

But this should be possible with the native interface.

Things like this, from the developer point of view looks like it should work (at least from my point of view :classic_smile:), we should not know that the interface comes from COM and all that history...so you can't do this and this an this...but...you can do all that things with the class that implements that interface. If you don't know the internals of the issue it's weird, and a little bit inconsistent...

There are probably considerations that those of us who are not experts miss along the way, but the usefulness seems clear

Share this post


Link to post

You are avoiding to answer the important question that is the reason why this does not work with COM interfaces.

  • Like 1

Share this post


Link to post
3 minutes ago, Stefan Glienke said:

You are avoiding to answer the important question that is the reason why this does not work with COM interfaces.

Methods (including interface methods) and method pointers are different language elements and they are not compatible. Technically, a method pointer is a TMethod element (Code+Data).

We are talking again about the particular way it has been implemented
Just for a moment, forget the implementation, and look at the question from the programmer perspective
Why should not be able to use an interface method as an event method? Behind every and each interface there is an object

Share this post


Link to post

For those of you who are more interested about the differences in class vs. interface inheritance, here is a transcript from several live chats with the founders of Delphi:

http://edn.embarcadero.com/article/20384

For those that don't want to read all (but it intersting anyway) here is the gist from Chuck Jazdzewski :

Quote

"The reason is a little complicated, but it is for compatibility with COM. COM does not assume that if you implement a descendent of an interface that it qualifies for an implementation of the descendent. This is because you are allowed to implement the ancestor completely different from the ancestor. To support this we cannot just assume you have implemented an ancestor. Also, you might not want to support the ancestor, only the descendent. If we always assumed you implemented the ancestor as well there would be no way to declare you didn't want the ancestor."

 

  • Like 1
  • Thanks 2

Share this post


Link to post
11 hours ago, Koru said:

IMyTest= nativeinterface
   ['{16E3EB88-4654-495A-9699-AC9FA2612352}']
   procedure DoProc;
   function DoFunc: Boolean;
end;

 

Why would you need a GUID if this is supposed to be "COM free"?

  • Like 1

Share this post


Link to post
Just now, Anders Melander said:

Why would you need a GUID if this is supposed to be "COM free"?

copy/paste problem, sorry 😅

Share this post


Link to post
12 minutes ago, M.Joos said:

here is the gist from Chuck Jazdzewski

Thank you!

This is in line with my understanding of the rules of COM. It seems that, unlike Kaster (it's a bug, it's a bug), Chuck (it's by design) actually read the books.

  • Like 1

Share this post


Link to post
4 minutes ago, Anders Melander said:

unlike Kaster (it's a bug, it's a bug), Chuck (it's by design) actually read the books.

You know that bugs can be in implementation and also in design

 

See this code:

 

type
  IFoo = interface
    ['{1E77F313-1C93-4A59-957B-751FB23EC63F}']
    procedure Foo;
  end;

  IBar = interface(IFoo)
    ['{49FC3BDA-7A09-4596-A80C-5EBEF73BA201}']
    procedure Bar;
  end;

  TFooBar = class(TInterfacedObject, IFoo, IBar)
  public
    procedure FooFoo;
    procedure IFoo.Foo = FooFoo;
    procedure BarFoo;
    procedure IBar.Foo = BarFoo;
    procedure Bar;
  end;

{ TFooBar }

procedure TFooBar.Bar;
begin
  Writeln('Bar');
end;

procedure TFooBar.BarFoo;
begin
  Writeln('BarFoo');
end;

procedure TFooBar.FooFoo;
begin
  Writeln('FooFoo');
end;

var
  bar: IBar;
  foo1, foo2: IFoo;
begin
  bar := TFooBar.Create;
  bar.Foo;
  bar.Bar;
  foo1 := bar;
  foo1.Foo;
  foo2 := bar as IFoo;
  foo2.Foo;
  Readln;
end.

The Foo method is implemented differently for IFoo and IBar but depending on how I get an IFoo I get one or the other.

Now we can run circles around this and argue back and forth - fact is that they clearly missed the opportunity to specify if you want to also let the compiler implicitly put the ancestors into the interface table as well if you really wanted to without explicitly writing them down - you have the implement the methods anyway.

  • Like 1

Share this post


Link to post
14 hours ago, Stefan Glienke said:

Yes! In the COM implementation and it was carried over to Delphi as a feature to make it work.

Do you have any sources for this bug in COM or can you explain what bug is?

Edit: Never mind. You just did.

Edited by Anders Melander
  • Like 1

Share this post


Link to post
3 minutes ago, Anders Melander said:

Do you have any sources for this bug in COM or can you explain what bug is?

It's mentioned in the "A bit of history" paragraph of the article you linked to yesterday - it's as if you write code like this

 

if Sender is TComponent then
  //... do something
else if Sender is TButton then
  //... do something else

You clearly will never reach the code for TButton

 

Clearly the interface inheritance clashes with the fact that you can implement a derived interface but not the base one.

 

Probably this is why COM in .NET behaves a bit differently:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/7313191a-10db-4a16-9cdd-de9fb80b378a/com-interop-base-class-properties-not-exposed-to-com?forum=csharpgeneral

 

 

Edited by Stefan Glienke
  • Like 1

Share this post


Link to post
40 minutes ago, Stefan Glienke said:

The Foo method is implemented differently for IFoo and IBar but depending on how I get an IFoo I get one or the other.

Sorry to continue this but I simply don't get it; Your example works exactly as I would expect. You're telling the compiler to use two different implementations for IBar.Foo and IFoo.Foo.

 

Even though interface B inherits from A and we have an object that implements both A and B, their shared methods (the ones from A) need not be the same (but they can be if you choose that). In fact COM explicitly states that it's the API that is inherited, not the implementation.

 

Your example more or less corresponds to:

type
  IFoo = interface
    ['{1E77F313-1C93-4A59-957B-751FB23EC63F}']
  end;

  IBar = interface(IFoo)
    ['{49FC3BDA-7A09-4596-A80C-5EBEF73BA201}']
  end;

  TFooBar = class(TInterfacedObject, IFoo, IBar)
  private
    FFoo: IFoo;
    FBar: IBar;
  public
    property Foo: IFoo read IFoo implements IFoo;
    property Bar: IBar read FBar implements IBar;
  end;

 

 

42 minutes ago, Stefan Glienke said:

It's mentioned in the "A bit of history" paragraph of the article you linked to yesterday

That's a secondary source. I'm looking for a primary one or at least a more reputable one.

Share this post


Link to post
1 hour ago, Anders Melander said:

In fact COM explicitly states that (...)

We're missing a main point: COM rules doesn't apply here

 

The proposal is not about changing the current interface implementation; that one is fine for COM, and for that same reason is not as good as it should be just for decoupling under Delphi, as it just can't implement some every-day features we do have on real life code (ej. Event methods)

 

The proposal is about having a new and independent breed of interfaces, able to implement the needs of current Delphi

So we forget all and every bit related to COM, and keep the concept and how we Delphians would expect it to work

So we expect using it with generics, and be able to use interface methods as class methods

  • Like 1

Share this post


Link to post

There is no proposal - there is just a "it would be cool if" without any actual suggestions how something can be achieved.

I asked a couple of questions but they have not been answered (or I missed those) that are important to decide if something like event<->interface method binding can be achieved and how it would look like.

Currently any assignment of interface methods to an event would break reference counting similar to how anonymous methods can't be assigned to an event. One is a managed type, the other is a record with 2 raw pointers.

 

Now we could all list stuff that would be cool to have without actually evaluating if that is technically possible - but to me that would be just a waste of time I could spend more productive.

  • Like 3

Share this post


Link to post
2 hours ago, Stefan Glienke said:

There is no proposal - there is just a "it would be cool if" without any actual suggestions how something can be achieved.

I asked a couple of questions but they have not been answered (or I missed those) that are important to decide if something like event<->interface method binding can be achieved and how it would look like.

Currently any assignment of interface methods to an event would break reference counting similar to how anonymous methods can't be assigned to an event. One is a managed type, the other is a record with 2 raw pointers.

 

Now we could all list stuff that would be cool to have without actually evaluating if that is technically possible - but to me that would be just a waste of time I could spend more productive.

Yes, we are asking for a wish list, without knowing the technical details. The context is 'Embarcadero says they can do it', and we look for ideas that come from people who have stuck with current interfaces, know the limitations they have and would like to solve. All this knowing that then Embarcadero will probably do it his way. But who knows ... maybe they'll hear us on something. And that was the idea.

 

I am sorry that we do not know how to help you more in the technical section, because we only wanted to move in the ideas leaving aside how it is done. In another post Javier says 'there is already an embarcadero answer on Quality portal, acknowledging this is feasable, and would work as expected'...reason why we believe that the technical aspect is solved already by Embarcadero, but any doubt that there is in this regard can also be presented to Embarcadero for clarification if possible, with specific questions.

 

So I understand what you ask Stefan, and maybe there is some detail in the comments of the link at the end that clarifies you something, or even you can ask any doubt there directly (they are answering all our questions so far): https://quality.embarcadero.com/browse/RSP-27911

Share this post


Link to post
1 hour ago, Koru said:

The context is 'Embarcadero says they can do it',

Yeah sure... Good luck with that - there are tons of more frequently asked and easier to design and implement features open for years.

I am very certain that Bruneau had the C++ approach in his mind even though he mentioned .NET/Java (which have GC that Delphi does not have) because otherwise memory management would clash.

 

And if I am not totally mistaken after a bit of search you cannot even implement more than one interface per class in C++ - but again my practical C++ experience is many years ago.

 

Now C# and Java can implement multiple interfaces per class and under the hood interfaces are basically the same as classes just that you cannot instantiate them. But the compiler and the runtime know about all that - if you think Embarcadero can pull this off you must be new to the game.

  • Like 1

Share this post


Link to post
10 hours ago, Stefan Glienke said:

Yeah sure... Good luck with that - there are tons of more frequently asked and easier to design and implement features open for years.

I am very certain that Bruneau had the C++ approach in his mind even though he mentioned .NET/Java (which have GC that Delphi does not have) because otherwise memory management would clash.

 

And if I am not totally mistaken after a bit of search you cannot even implement more than one interface per class in C++ - but again my practical C++ experience is many years ago.

 

Now C# and Java can implement multiple interfaces per class and under the hood interfaces are basically the same as classes just that you cannot instantiate them. But the compiler and the runtime know about all that - if you think Embarcadero can pull this off you must be new to the game.

Unfortunately we are nothing new with Delphi, although we may seem so, but with a halo of hope.
Ok, the opinions are clear, we continue with the fight on our own. Thank you very much to all

Share this post


Link to post
Guest

Here another idea, and it is not about helpers for interfaces, but to a cross between them.

Interfaces are limited, and hindering the way you declare objects (that is the point after all) , means compiler should have the interface first then the Object/Class declaration.

Helpers are more relaxed, but yet you should have the Object/Class first then the helper, and that will be guaranteed by the compiler, and only one with ancestor, also scope is playing role here.

 

So, what about something hybrid like this 

type
  ILogger = interface    //(IInterface)
    helper function CreationTime: TDateTime;
    helper procedure LogSelf;
    procedure SomeExtraLogging;
  end;

{ ILogger }
helper function ILogger.CreationTime: TDateTime;
begin
  Result := FCreationTime;    // will behave like helper does, FCreationTime should be a field in the object
end;

helper procedure ILogger.LogSelf;
begin
  DoLog(Self, CreationTime); //    Self here belongs to the Object/Class, and yet CreationTime is still accessible 
end;

With helper functions/procedures the interface will still behave like always it did, but when it comes to helper methods they will be resolved at compile time to handled as helper per Class, means all what compiler have to do is to delay the helpers methods until the interface been used in an object

 

Advantages:

1) No backward compatibility is broken, no change to existing code or the old style.

2) Helpers with such declaration will be even more helpful, as you can now use multiple from any scope (close or far).

3) Shorter code in Objects/Class, means less complexity and more clarity, the compiler will fail to compile when interface helper method been used and doesn't belongs to according Object, it should fail on the object not the interface, same as failing to compile object without according interface method, and same as it will fail of a helper tried to access non existed fields/method.

Share this post


Link to post
47 minutes ago, Kas Ob. said:

So, what about something hybrid like this 

 

One thing worth mentioning because you seem to take interface-class relation backwards.

 

The whole purpose of interfaces (COM or native, or whatever) is that they are abstraction. They just define API, not the implementation. It is class that implements interface and knows about the interface, not the other way around. 

 

In other words, interface should never ever know anything about classes that implement interface and any proposals that violate that are practically useless.

  • Like 5

Share this post


Link to post
1 hour ago, Kas Ob. said:

Here another idea, and it is not about helpers for interfaces, but to a cross between them.

What you display is known as "default interface methods" in other languages. Java has them and they are in the making for C#8 from what I know.

However in both implementations those interfaces can just operate on other interface methods and not force fields of the implementing object - that would be more like scala traits (not exactly sure how they implement that ontop of the JVM)

  • Like 2

Share this post


Link to post
Guest
10 hours ago, Dalija Prasnikar said:

They just define API, not the implementation. It is class that implements interface and knows about the interface, not the other way around. 

In other words, interface should never ever know anything about classes that implement interface and any proposals that violate that are practically useless.

One thing though, the point of having syntax in a language is to make written in a stone or let it evolve to something better, shorter, may be just more productive.

 

The way interfaces is declared and used in current state is hindering there usage and somehow counter productive (i know what they serve) , so if you are writing a layer of lets say file management (many classes and unit files), then most likely you will prefer to expose only the outer interface of that layer in whole, but internally that file handling layer really should not have interfaces, this will just waste of time specially in adding features later and will be prone to errors and just huge waste of time, like where interfaces doesn't have inheritance, so more interfaces are needed.

 

Yes, i understand the importance and usefulness of abstraction, but can an abstracted layer used in its own code to serve multi purposes ?

Can the outer interface be minimized to be extracted from a class, instead of be forced to build upon it, just different way of thinking, and one might take in either way, i just love to have more choices, a with shorter and clearer code where i allow change in one place instead of two.

Look at that example, the logger it might had nothing to do with objects and yet it can have all access if one want it, it is simply an idea to evolve, after how this is far away from how generics came to a language, if you go back and look at the past and see the same logic goes like here, a class is declared as...., and in generics a type is declared ... contradicting class declaration ( it is not a contradiction now) , try to imagine the discussion about generics when they started as an idea, i am sure many said it is good but al so many said it is wrong and will bring only errors, so this can go either way.

 

It was an idea, simply put, just food for thought in a thread i think can be very useful as think tank to discuss evolution of interface, if that will ever happen.

 

Here another example to make my point little clearer.

 

Lets take an example a transport layer (TCP and the ability to switch to HTTPS or even DTLS), many classes and units involved, the request was to have interface of it, then i did an interface to end user ( other developers) , but internally it has 0 benefits for me inside that layer, only wasting my time while developing and testing might take very long time, many things might/will change, interfaces will waste my time, but on other hands i like helpers and they are very useful and used in many places, now combine them both, and we got powerful code syntax, personally i prefer such interface-helper hybrid to force myself into discipline, as my wasted time on helpers is almost the same will be on those new type interfaces. 

Share this post


Link to post

Having these discussions on desired language features bear little effect unless a properly thought through and well written proposal is submitted through QP, and even then it is just a pipe dream until it gathers a large number of votes, and/or someone within EMBT agrees that it is a good idea.

 

Keep the ideas coming, and do discuss them - but unless they are formally submitted, we are just having a chat by the watercooler, if you see the analogy?

  • Like 4

Share this post


Link to post
Guest
1 hour ago, Lars Fosdal said:

we are just having a chat by the watercooler

If just cool water killed the bugs ! instead of compiler drooling mixed with my coffee,..  took me long time to stop punching the monitor and take on habit of splashing coffee on it, it does yield better effect, try it in the evening for best result.

 

I would suggest three separated threads, RTL, Compiler and Language itself ( thinking in loud voice a decision about naming will need an overhead of 5-7 threads and... we are OK) 

Each thread will will have its own scope ( like compiler stuff belongs to compiler thread but it might be mentioned as reference in other posts), discussion is about requests, features and something you like to see, ideas in general is OK too, with of course listing the advantages, one feature per post to make it easy to divide and conquer ( i mean give it deep and well deserved think time), such threads can be helpful understand Delphi itself while serve as historical reference when Embarcadero guys feel bored and looking for ideas, or someone submit it while having them discussed here might have high vote rate. 

 

 

Share this post


Link to post
Guest

I have few ideas that might worth discussing like this one that will belongs to Language or Compiler, yet to be seen.

 

Different variable declaration, something new like mvar (managed) or svar (safe), to be used like this

procedure LoadComaSeparated(const aFileName: string);
var
  C: Integer;
mvar                      // managed and intialized varaibles, that will initialized to default,eg. integer = 0 , boolean to false etc
  StrList: TStringList;   // this will be created automatically and compiler hide the create try..finally free
                          // as long the contructor had default parameters or no parameters at all, just like local string
begin
  StrList.LineBreak := ',';
  StrList.LoadFromFile(aFileName);
end;

How nice is that ?, with no compatibility issue with anything written with Delphi, also it is easy to implement.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×