Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2026-02

interfaces / inheritance / again -

11 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin Frb

 Not yet tested, but only matters if it is not going to be discontinued (in case it actually works)

I want a
  generic Foo <_A: SomeIntf;  _B: TBar>

such as that I can give an inherhited interface.

The interface has
  GetBar => which return TBar => or subclass....
Hence for the generic I have a param to specify the class.

But, I cans do
IFoo = interface
  proceduer GetBar: TBar;

ISubFoo = interface(Foo)
   proceduer GetBar: TSubBar;

However, (tests pending) if the below works, would it be working by indent? And therefore not removed by some bug fix in future?

IFoo = interface
   proceduer GetBar: TBar;
   property Bar: TBar;
ISubFoo = interface(Foo)
   proceduer GetSubBar: TSubBar;
   property Bar: TSubBar;

The property is just syntactic sugar isn't it?
But the generic would see it according to the actual type of the gen param.

Reply by email

Michael Van Canneyt
to Martin Frb
Martin Frb wrote 19 lines
 Not yet tested, but only matters if it is not going to be discontinued (in case it actually works) I want a   generic Foo <_A: SomeIntf;  _B: TBar> such as that I can give an inherhited interface. The interface has   GetBar => which return TBar => or subclass.... Hence for the generic I have a param to specify the class. But, I cans do IFoo = interface   proceduer GetBar: TBar; ISubFoo = interface(Foo)   proceduer GetBar: TSubBar;

Are you deliberately trying to shoot yourself in the foot here ? (better your foot than my foot obviously...)

Show quoted text 7 lines
However, (tests pending) if the below works, would it be working by indent? And therefore not removed by some bug fix in future? IFoo = interface   proceduer GetBar: TBar;   property Bar: TBar;

I assume this should be:

property Bar: TBar read GetBar;
ISubFoo = interface(Foo)   proceduer GetSubBar: TSubBar;   property Bar: TSubBar;

Similarly, I assume you mean

  property Bar: TSubBar read GetSubBar;

property overrides exist, but they must keep the same type, they can only change the specifiers. So I don't think this will compile. If it does, it is probably by accident.

The property is just syntactic sugar isn't it? But the generic would see it according to the actual type of the gen param.

I don't know, but to me it looks like a definite way to confuse yourself and others.

I would define this as

   ISubFoo = interface(Foo)
     procedure GetSubBar: TSubBar;
     property SubBar: TSubBar;
   end;

Michael.

Reply by email

Martin Frb
to Michael Van Canneyt
Michael Van Canneyt wrote
Are you deliberately trying to shoot yourself in the foot here ? (better your foot than my foot obviously...)

Well, the simplest would be

   generic TGen<DATA>

no constraint, I can pass in whatever, and if it has the methods, then
good ... (or well...)

But, that feels more like risking my foot. I quite prefer if I can give a constraint, and if the compiler will know earlier what the generic is about.

I have a list. Well several lists. They share some functionality. But I can't just pack it into a base class.

For once, at least in some parts of the code, the list will only be known as "interface".

The lists have different item classes too.  So now I thought I write a generic that implements the shared bit.
Actually the shared bit is in another class, that accepts the list as argument. A worker class, if you will.

generic SharedBit<_BASE, _LIST, _ITEM> = class(_BASE)
   procedure ProcessItem(Itm: _ITEM);  virtual; // called by DoFoo
   procedure DoFoo(List: _LIST);
end;
procedure DoFoo(List: _LIST);
begin
   ProcessItem(List.Entries[i]);  // Entries must return the specific
interface
end

ProcessItem must have the subclass, or at least the more specific interface.

And yes, I could add to the interface
  function GetObject: TObject
and then type cast that.
That feels like a bandaid.

Since I do have some code that has the list as interface, the simplest is that all code that does SharedBit uses the interfaces. (Well, yes, leave away the constraint, and I can specialize for either class or interface)

I assume this should be: property Bar: TBar read GetBar;

yes

Show quoted text 5 lines
 property Bar: TSubBar read GetSubBar; property overrides exist, but they must keep the same type, they can only change the specifiers. So I don't think this will compile. If it does, it is probably by accident.

It does actually compile.

And on classes, properties can be reintroduces with any changes possible (wouldn't necessarily do but works)

On interfaces, it does not conflict with getting the interface by it GUID, or with what the interface "contains" => i.e. its definition, as it would be seen by any other code, e.g. a 3rd party lib. The "property" doesn't change the VMT of the interface.

It really just defines an alias within the type name of the interface (almost like a type helper).

The inherited interface, now introduces a new method, with a new name, and a new type. Completely independent method. The parent VMT has nothing hidden, all fine so far.

The property just changes the alias, which function to call. And its not bound to the vmt, it hangs on the type name of the interface (at least my understanding).

I don't know, but to me it looks like a definite way to confuse yourself and others.

Not to much confusion. It returns the same entry, but with a different type.

Well, ok. its a different interface, but the object behind it is the same object. same instance. And the returned interface inherits from the original interface too (even though that isn't enforced by the compiler)

So functional it does the same thing. It just offers additional methods.

And those added methods are what I really need to access in the overridden "ProcessItem".

Show quoted text 7 lines
I would define this as   ISubFoo = interface(Foo)     procedure GetSubBar: TSubBar;     property SubBar: TSubBar;   end;
Except, I must have it as
       property Bar: TSubBar;

=> because the code in the above generic will access "Bar".

I can pass the type for it (for the param in the virtual ProcessItem) to the generic.

But I can not pass the name of the property to the generic. The generic will always call "Bar"

Reply by email

Michael Van Canneyt
to Martin Frb
Martin Frb wrote 71 lines
On 02/02/2026 20:48, Michael Van Canneyt via fpc-devel wrote: > > Are you deliberately trying to shoot yourself in the foot here ? > (better your foot than my foot obviously...) Well, the simplest would be    generic TGen<DATA> no constraint, I can pass in whatever, and if it has the methods, then good ... (or well...) But, that feels more like risking my foot. I quite prefer if I can give a constraint, and if the compiler will know earlier what the generic is about. I have a list. Well several lists. They share some functionality. But I can't just pack it into a base class. For once, at least in some parts of the code, the list will only be known as "interface". The lists have different item classes too.  So now I thought I write a generic that implements the shared bit. Actually the shared bit is in another class, that accepts the list as argument. A worker class, if you will. generic SharedBit<_BASE, _LIST, _ITEM> = class(_BASE)   procedure ProcessItem(Itm: _ITEM);  virtual; // called by DoFoo   procedure DoFoo(List: _LIST); end; procedure DoFoo(List: _LIST); begin   ProcessItem(List.Entries[i]);  // Entries must return the specific interface end ProcessItem must have the subclass, or at least the more specific interface. And yes, I could add to the interface   function GetObject: TObject and then type cast that. That feels like a bandaid. Since I do have some code that has the list as interface, the simplest is that all code that does SharedBit uses the interfaces. (Well, yes, leave away the constraint, and I can specialize for either class or interface) > > I assume this should be: > > property Bar: TBar read GetBar; yes >  property Bar: TSubBar read GetSubBar; > > property overrides exist, but they must keep the same type, they can only > change the specifiers. So I don't think this will compile. If it does, it > is probably by accident. It does actually compile. And on classes, properties can be reintroduces with any changes possible (wouldn't necessarily do but works)

Yes, I forgot, Delphi allows this. It's a redeclaration, not an override. (I always thought that was a bad idea)

Well, you have your answer. It compiles and probably should compile, at least in mode delphi.

Whether it is a good approach, there we probably differ in opinion :-)

Michael.

Reply by email

Martin Frb
to Michael Van Canneyt
Michael Van Canneyt wrote
Whether it is a good approach, there we probably differ in opinion :-)

Well, the good approach doesn't work.

interface can't have virtual/override apparently (it can but ignores).

In a class, an overridden function can actually return a subclass of what the virtual method returned.
And well, why not, it fulfills all the need.

If that was possible in an interface too, then all would be good

IFoo = interface
   function Itm: TItem; virtual;
ISubFoo = interface(IFoo)
   function Itm: TSubItem; override;

ISubFoo fulfils every bit of the contract of its base. But this does not work, so I need to look for something else.

You read my description. Ideas?

Other than just "drop the constraint" => then yes, it works, because I have completely independent interfaces, no inheritance, just the same name for 2 functions that have diff return types.... I can't however see, how that is better.

Copy and paste the code... That might have been the solution before generics.

Not using interfaces at all => but this code includes (not limited but includes) stuff that is an API for 3rd party. Really want that as an interface.

Reply by email

Michael Van Canneyt
to Martin Frb
Martin Frb wrote 12 lines
On 02/02/2026 22:00, Michael Van Canneyt via fpc-devel wrote: > > Whether it is a good approach, there we probably differ in opinion :-) Well, the good approach doesn't work. interface can't have virtual/override apparently (it can but ignores). In a class, an overridden function can actually return a subclass of what the virtual method returned.

I don't understand this remark. Please explain ?

Show quoted text 6 lines
And well, why not, it fulfills all the need. If that was possible in an interface too, then all would be good IFoo = interface   function Itm: TItem; virtual;

Why would an actual implementation not be able to return TSubItem ?

Show quoted text 8 lines
ISubFoo = interface(IFoo)   function Itm: TSubItem; override; ISubFoo fulfils every bit of the contract of its base. But this does not work, so I need to look for something else. You read my description. Ideas?

Perhaps stupid question: why don't you use a generic IFoo<T> ?

based on your description, I don't understand what you're trying to accomplish.

I read your technical solution to a problem: all that is clear to me is that you want to use generics and interfaces to mix lists, but I did not understand the original problem: why do you need these lists, what is their purpose etc.

Maybe you don't need a simple iterator or visitor pattern suffices, and you would not have the complexity you introduce by using lists. Or indeed copying or using an include file and a macro is all you can do: Generics don't solve all problems, they're overused IMO. And in my experience interfaces introduce more problems than that they solve, plus they don't mix well with generics (no generic methods in interfaces).

In short: for me, it is hard to say what is best, based on what you gave as info.

Michael.

Reply by email

Martin Frb
to Michael Van Canneyt
Michael Van Canneyt wrote 10 lines
On Mon, 2 Feb 2026, Martin Frb via fpc-devel wrote: > > > In a class, an overridden function can actually return a subclass of > what the virtual method returned. I don't understand this remark. Please explain ?

See below, the overriden method returns a subclass. (works with subclass as result too).

program Project1;
{$Mode objfpc}{$Interfaces corba}
type
   IFoo = interface end;
   IFoo2 = interface(IFoo) end;
   TBar = class
     function abc: IFoo; virtual;
   end;
   TBar2 = class(TBar)
     function abc: IFoo2; override; // inherited interface as result type
   end;
function TBar2.abc: IFoo2;
begin
end;
function TBar.abc: IFoo;
begin
end;
begin
end.

Reply by email

Martin Frb
to Michael Van Canneyt: I don't understand this remark. Please explain ? Why would an actual implementation not…
Michael Van Canneyt wrote
Perhaps stupid question: why don't you use a generic IFoo<T> ?

Because I can't use that as constraint to the generic that requires the different return types.

If I drop the constraints, then I can just use independent interfaces... But then still, each interface has a
  function    GetItem: TSomeType;  // diff type for each interface

And the generic will call that GetItem, on all of them. Not much difference.

Show quoted text 7 lines
I read your technical solution to a problem: all that is clear to me is that you want to use generics and interfaces to mix lists, but I did not understand the original problem: why do you need these lists, what is their purpose etc.

See sample at the end... May have some issues. I had to rewrite it countless times. I do have to double check that the current version makes sense.

I will then have different list, that can be synced.

Some of them have refcounting items. In that case the code in an inherited method "UpdateEntryAt" must be able to access   data_of_type_SRC_ENTRY.AddReference

Of course I can
  specialize TDbgSynchronizedListTemplate<TOBject, _LIST, IIntfForItemWithAddRef>

But then if
  List.Entries does not return the type IIntfForItemWithAddRef that is where the problem is.

I can specialize the generic with the correct inherited interface. But that interface will have
  _LIST.Entries[i]
called.
The name "Entries" is hard coded, I can not change that. (Oh, yes, add a virtual method "function GetEntry: _SRC_ENTRY; virtual", and override it in each specialization. Not really, what I was looking for.

Maybe you don't need a simple iterator or visitor pattern suffices, and you would not have the complexity you introduce by using lists.

The lists will contain data for the debugger.  Each backend will have a list. And the frontend will have one.

So there is my need for interfaces, allowing me to separate the backend and frontent.
The backends need copies, so they can keep old data until they processed the new. Or they may have to make copies to be handled in threads...

So currently I only have one set of data...  (yet already storage with and without refcounting)
But the concept of such a list, is generally useful, and I can see it being used more on the long run... So I do want to separate the "sync" concept, and the payload.

Also depending on other payloads, the storage implementation may vary: list, hash ....

------------------------------------------------------------

2 (or a chain of more than 2) lists, that can sync to updates of the "origin" (or multiple origin lists).

Entries are marked with a global change stamp. So lists only need to update entries that are changed, new or gone.
(meaning for other entries, there is no search for the copied entry, saving time)

The "receiving" class is based on the generic.
it can itself also implement the interface, do allow forward sync to yet another receiver.

type

  (* About the ChangeStamp:

     Each type should have one global source for the ChangeStamp.      If any entry changes, it should get a new value, that therefore is newer
     than any known value by any list that may exist (and that may compare to
     the entries ChangeStamp)

     If a List updates it can either set its own ChangeStamp to the highest seen
     value in all entries, or to the current global value.

     If an entry is added/removed to/from a list (that can be source for sync)
     then that entry must update its ChangeStamp (even if it had no changes of
     its own).
     That way it will be noted as new by any list that may synchronize from it,
     even if that list had not known the entry before.

     If a list is not used a source for sync, then add/remove of entries does not
     need to update the entries ChangeStamps
  *)

  IDbgSynchronizedListEntryIntf = interface
['{A05705C7-7443-43A0-9B32-EDAD5D888051}']
    function  GetID: QWord;
    function  GetChangeStamp: QWord;
    function  IsDeleted: boolean;      // check in source list, remove in target

     property ID: QWord read GetID;
     property ChangeStamp: QWord read GetChangeStamp;
   end;
   IDbgSynchronizedListIntf = interface
['{3DA08DFE-B061-499C-9FA0-8ECA57F19496}']
     function  Count: Integer;
     function  GetSyncEntry(AnIndex: Integer):
IDbgSynchronizedListEntryIntf;
     function  GetChangeStamp: QWord;
     property Entries[AnIndex: integer]: IDbgSynchronizedListEntryIntf
read GetSyncEntry; default;
     property ChangeStamp: QWord read GetChangeStamp;
   end;

  { TDbgSynchronizedListTemplate }

  generic TDbgSynchronizedListTemplate<
    _BASE: class;
    _SRC_LIST: IDbgSynchronizedListIntf;
    _SRC_ENTRY: IDbgSynchronizedListEntryIntf
  > = class(_BASE)
  private
    procedure UpdateOwnCountAfterDelete(AnOwnCount: integer); virtual;   protected
    function  GetIndexOfId(AnID: QWord): Integer; virtual; abstract;

     procedure AddEntryFor(AnSource: _SRC_ENTRY); virtual; abstract;
     procedure UpdateEntryAt(AnIndex: integer; AnSource: _SRC_ENTRY);
virtual; abstract;
     procedure DeleteEntryAt(AnIndex: integer; AnSource: _SRC_ENTRY);
virtual; abstract;
     procedure UpdateListFrom(ASourceList: _SRC_LIST; AnOwnCount:
integer; var AnOwnChangeStamp: QWord);
   end;

Reply by email

Martin Frb
to Martin Frb
Martin Frb wrote 9 lines
If I drop the constraints, then I can just use independent interfaces... But then still, each interface has a   function    GetItem: TSomeType;  // diff type for each interface And the generic will call that GetItem, on all of them. Not much difference.

I could probably just have

function Item: IBaseIntf

and return the inherited inteface as IBaseIntf, and on the receiving end (in the generic, and other code) typecast it back... (not tested, but should work).

But then I can also just drop the constraints...
I am trying to ditch as little of the available type as possible.

Reply by email

Michael Van Canneyt
to Martin Frb: See below, the overriden method returns a subclass. (works with subclass as result too).…
Martin Frb wrote 35 lines
On 02/02/2026 22:59, Michael Van Canneyt via fpc-devel wrote: > > > On Mon, 2 Feb 2026, Martin Frb via fpc-devel wrote: > >> >> >> In a class, an overridden function can actually return a subclass of >> what the virtual method returned. > > I don't understand this remark. Please explain ? See below, the overriden method returns a subclass. (works with subclass as result too). program Project1; {$Mode objfpc}{$Interfaces corba} type   IFoo = interface end;   IFoo2 = interface(IFoo) end;   TBar = class     function abc: IFoo; virtual;   end;   TBar2 = class(TBar)     function abc: IFoo2; override; // inherited interface as result type   end; function TBar2.abc: IFoo2; begin end; function TBar.abc: IFoo; begin end;

2 remarks

- Does not compile in Delphi.

- Please use 'plain text' format for your mails. I can't copy & paste this code and compile: it is full of unicode spaces and whatnot. I needed a hex dump to understand what was happening.

Michael.

Reply by email

Martin Frb
to Martin Frb: I could probably just have function Item: IBaseIntf and return the inherited inteface as…
Martin Frb wrote
I am trying to ditch as little of the available type as possible.

I may have found a solution...

I can decouple the interface for the item sync-stuff and the payload. The latter does not need to inherit from the first.

I have to see if it fully works, I then need to use the index to get from the sync interface to the payload, in order to update the payload. But the index is always integer, so it does not have the problem of changing.

Need to see, how exactly that can be done.
Or I add the payload as empty interface (returned by function), and have to cast it... (I don't like that, but...)

Reply by email