Sign in Join

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

Mailing lists / fpc-pascal / 2008-05

casting interfaces and objects

13 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

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

Marc Santhoff

Hi,

I have two questions about interface usage.

1. How interfaces are to handle when casting?
Theoretically it should be safe to do this:

TObservable = class
public
  procedure Register( obsv: IObserver );
private
  procedure NotifyObservers(param: TParameter);
  fObservers: TFPObjectList;
end;
procedure TObservable.Register( obsv: IObserver );
begin
	fObservers.add(TObject(obsv));
end;

When using a cast from an interface to an object the compiler warns about the class types "TObject" and "IObserver" not being related.

Since an implementor of in interface always will be a class type and any class type at least inherits from TObject, I assume it is okay?

Is there a better method of handling this case?

2. Will an interface and a class with the same signature but not explicitely implementing this interface be runtime-compatible and interchangably usable?

Smth. like this:

IObserver = Interface
  procedure Notify( param: TParameter );
end;
TObserver = class
public
  procedure Notify( param: TParameter ); virtual; abstract;
end;

Is it legal to use one or the other when e.g. handing over an observer to a method having an IObserver as argument?

TIA,
Marc

Graeme Geldenhuys
to Marc Santhoff

2008/5/18 Marc Santhoff :

Show quoted text 7 lines
When using a cast from an interface to an object the compiler warns about the class types "TObject" and "IObserver" not being related. Since an implementor of in interface always will be a class type and any class type at least inherits from TObject, I assume it is okay? Is there a better method of handling this case?

Casting of Interfaces has always been known to me as a big NO-NO. I believe it's got something to do with how they are represented in the VMT or method table or something like that. I don't know the compiler internals. Why not simply use a InterfaceList or a generic Pointer list (as to not increment the interface reference counter). You can then add them to the list without having to cast to TObject.

Regards,
- Graeme -

_______________________________________________
fpGUI - a cross-platform Free Pascal GUI toolkit
http://opensoft.homeip.net/fpgui/

Joao Morais
to Marc Santhoff: I have two questions about interface usage. 1. How interfaces are to handle when casting?…
Marc Santhoff wrote 19 lines
Hi, I have two questions about interface usage. 1. How interfaces are to handle when casting? Theoretically it should be safe to do this: TObservable = class public procedure Register( obsv: IObserver ); private procedure NotifyObservers(param: TParameter); fObservers: TFPObjectList; end; procedure TObservable.Register( obsv: IObserver ); begin fObservers.add(TObject(obsv)); end;

It is safe to store, it isn't safe to use as an object -- you will need to cast back to IObserver to use the instance.

Show quoted text 5 lines
When using a cast from an interface to an object the compiler warns about the class types "TObject" and "IObserver" not being related. Since an implementor of in interface always will be a class type and any class type at least inherits from TObject, I assume it is okay?

No no, they are quite different.

Is there a better method of handling this case?

Use TInterfaceList or IInterfaceList.

2. Will an interface and a class with the same signature but not explicitely implementing this interface be runtime-compatible and interchangably usable?

No. You need to include the interface in the class declaration or make a workaround in the QueryInterface method.

Joao Morais

Show quoted text 22 lines
Smth. like this: IObserver = Interface procedure Notify( param: TParameter ); end; TObserver = class public procedure Notify( param: TParameter ); virtual; abstract; end; Is it legal to use one or the other when e.g. handing over an observer to a method having an IObserver as argument? TIA, Marc _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Marco van de Voort
to Marc Santhoff: I have two questions about interface usage. 1. How interfaces are to handle when casting?…
Show quoted text 17 lines
I have two questions about interface usage. 1. How interfaces are to handle when casting? Theoretically it should be safe to do this: TObservable = class public procedure Register( obsv: IObserver ); private procedure NotifyObservers(param: TParameter); fObservers: TFPObjectList; end; procedure TObservable.Register( obsv: IObserver ); begin fObservers.add(TObject(obsv)); end;

No it is not safe. The interface points to an interface table, and you cast it if it were a class VMT.

Have a look at IInterfaceComponentReference and use/implement the getcomponent method. Note that this was added post-2.2.0

Marc Santhoff
to Joao Morais: It is safe to store, it isn't safe to use as an object -- you will need to cast back to…
Joao Morais wrote 23 lines
Marc Santhoff wrote: > Hi, > > I have two questions about interface usage. > > 1. How interfaces are to handle when casting? > Theoretically it should be safe to do this: > > TObservable = class > public > procedure Register( obsv: IObserver ); > private > procedure NotifyObservers(param: TParameter); > fObservers: TFPObjectList; > end; > > procedure TObservable.Register( obsv: IObserver ); > begin > fObservers.add(TObject(obsv)); > end; It is safe to store, it isn't safe to use as an object -- you will need to cast back to IObserver to use the instance.

As always.

Show quoted text 7 lines
> When using a cast from an interface to an object the compiler warns > about the class types "TObject" and "IObserver" not being related. > > Since an implementor of in interface always will be a class type and any > class type at least inherits from TObject, I assume it is okay? No no, they are quite different.

So that's why the compiler issues a warning.

> Is there a better method of handling this case? Use TInterfaceList or IInterfaceList.

That's nice, I only didn't know it until now.

Show quoted text 6 lines
> 2. Will an interface and a class with the same signature but not > explicitely implementing this interface be runtime-compatible and > interchangably usable? No. You need to include the interface in the class declaration or make a workaround in the QueryInterface method.

Which is a pretty clear solution, it's only important to give the programmer the opportunity to derive from a class or implement the interface himself.

Many thanks,
Marc

Marc Santhoff
to Graeme Geldenhuys: 2008/5/18 Marc Santhoff : Casting of Interfaces has always been known to me as a big…
Graeme Geldenhuys wrote 10 lines
2008/5/18 Marc Santhoff : > When using a cast from an interface to an object the compiler warns > about the class types "TObject" and "IObserver" not being related. > > Since an implementor of in interface always will be a class type and any > class type at least inherits from TObject, I assume it is okay? > > Is there a better method of handling this case? Casting of Interfaces has always been known to me as a big NO-NO.

I learned casting in general is sort of evil, but not always avoidable.

I believe it's got something to do with how they are represented in the VMT or method table or something like that. I don't know the compiler internals.

I see, enough to remember "Do not cast interfaces!". ;)

Why not simply use a InterfaceList or a generic Pointer list (as to not increment the interface reference counter). You can then add them to the list without having to cast to TObject.

That's what I will do.

Thank you,
Marc

Marc Santhoff
to Marco van de Voort: No it is not safe. The interface points to an interface table, and you cast it if it were…
Marco van de Voort wrote 20 lines
> I have two questions about interface usage. > > 1. How interfaces are to handle when casting? > Theoretically it should be safe to do this: > > TObservable = class > public > procedure Register( obsv: IObserver ); > private > procedure NotifyObservers(param: TParameter); > fObservers: TFPObjectList; > end; > > procedure TObservable.Register( obsv: IObserver ); > begin > fObservers.add(TObject(obsv)); > end; No it is not safe. The interface points to an interface table, and you cast it if it were a class VMT.

I see. I indeed had a somewhat "hacky" feeling using that cast ...

Since the compiler dod not warn about using the "as" operator I assume it is safe? Like this:

procedure TObservable.DeRegister( obsv: IObserver );
var
	i: integer;
begin
	if (fObservers.count<=0) then exit;
	for i:=0 to fObservers.count-1 do begin
		if ((fObservers[i] as IObserver)=obsv) then begin
			fObservers.delete(i);
			fObservers.pack;
			exit;
		end;
	end;
end;

(Although this example will no longer exist when using an T/IInterfaceList.)

Have a look at IInterfaceComponentReference and use/implement the getcomponent method. Note that this was added post-2.2.0

I'll have a look and put it on my todo-list.

Thanks a lot,
Marc

Marco van de Voort
to Marc Santhoff
Show quoted text 9 lines
Am Sonntag, den 18.05.2008, 23:07 +0200 schrieb Marco van de Voort: begin if (fObservers.count<=0) then exit; for i:=0 to fObservers.count-1 do begin if ((fObservers[i] as IObserver)=obsv) then begin fObservers.delete(i); fObservers.pack; exit;

I think this works, assuning fobservers is an array or tlist of objects, because you only go from object to interface, not from interface to object.

Graeme Geldenhuys
to Marc Santhoff: I see. I indeed had a somewhat "hacky" feeling using that cast ... Since the compiler dod…

2008/5/18 Marc Santhoff :

Show quoted text 10 lines
Since the compiler dod not warn about using the "as" operator I assume it is safe? Like this: procedure TObservable.DeRegister( obsv: IObserver ); var i: integer; begin if (fObservers.count<=0) then exit; for i:=0 to fObservers.count-1 do begin if ((fObservers[i] as IObserver)=obsv) then begin

I'm not a big Interfaces expert, but as for as I know using 'as' like that with Interfaces actually translates to a QueryInterface() call internally, which is why it is allowed. I vaguely remember as issue between CORBA and COM interfaces and the 'as' call, because CORBA interfaces do not have a QueryInterface() implementation. Well, something like that... :-)

Regards,
- Graeme -

_______________________________________________
fpGUI - a cross-platform Free Pascal GUI toolkit
http://opensoft.homeip.net/fpgui/

Marc Santhoff
to Graeme Geldenhuys
Graeme Geldenhuys wrote 19 lines
2008/5/18 Marc Santhoff : > Since the compiler dod not warn about using the "as" operator I assume > it is safe? Like this: > > procedure TObservable.DeRegister( obsv: IObserver ); > var > i: integer; > begin > if (fObservers.count<=0) then exit; > for i:=0 to fObservers.count-1 do begin > if ((fObservers[i] as IObserver)=obsv) then begin I'm not a big Interfaces expert, but as for as I know using 'as' like that with Interfaces actually translates to a QueryInterface() call internally, which is why it is allowed. I vaguely remember as issue between CORBA and COM interfaces and the 'as' call, because CORBA interfaces do not have a QueryInterface() implementation. Well, something like that... :-)

Okay, so there are still some unknowns.

However, before something goes into production I'll put up some tests for the cases in question.

Thanks again, to Marco too,
Marc

Graeme Geldenhuys
to Marc Santhoff
Marc Santhoff wrote 21 lines
Am Sonntag, den 18.05.2008, 23:41 +0200 schrieb Graeme Geldenhuys: > 2008/5/18 Marc Santhoff : >> Since the compiler dod not warn about using the "as" operator I assume >> it is safe? Like this: >> >> procedure TObservable.DeRegister( obsv: IObserver ); >> var >> i: integer; >> begin >> if (fObservers.count<=0) then exit; >> for i:=0 to fObservers.count-1 do begin >> if ((fObservers[i] as IObserver)=obsv) then begin > > I'm not a big Interfaces expert, but as for as I know using 'as' like > that with Interfaces actually translates to a QueryInterface() call > internally, which is why it is allowed. I vaguely remember as issue > between CORBA and COM interfaces and the 'as' call, because CORBA > interfaces do not have a QueryInterface() implementation. Well, > something like that... :-) Okay, so there are still some unknowns.

CORBA interfaces do not include the following methods. These are only relevant to COM interfaces.

function QueryInterface(const iid: tguid; out obj): longint; stdcall;
function _AddRef: longint; stdcall;
function _Release: longint; stdcall;

Everybody I know uses COM interfaces, except for one developers - Martin from MSEgui project. FPC defaults to COM style interfaces as well.

Regards,
- Graeme -

_______________________________________________________ fpGUI - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Marc Santhoff
to Graeme Geldenhuys
Graeme Geldenhuys wrote 29 lines
Marc Santhoff wrote: > Am Sonntag, den 18.05.2008, 23:41 +0200 schrieb Graeme Geldenhuys: >> 2008/5/18 Marc Santhoff : >>> Since the compiler dod not warn about using the "as" operator I assume >>> it is safe? Like this: >>> >>> procedure TObservable.DeRegister( obsv: IObserver ); >>> var >>> i: integer; >>> begin >>> if (fObservers.count<=0) then exit; >>> for i:=0 to fObservers.count-1 do begin >>> if ((fObservers[i] as IObserver)=obsv) then begin >> >> I'm not a big Interfaces expert, but as for as I know using 'as' like >> that with Interfaces actually translates to a QueryInterface() call >> internally, which is why it is allowed. I vaguely remember as issue >> between CORBA and COM interfaces and the 'as' call, because CORBA >> interfaces do not have a QueryInterface() implementation. Well, >> something like that... :-) > > Okay, so there are still some unknowns. CORBA interfaces do not include the following methods. These are only relevant to COM interfaces. function QueryInterface(const iid: tguid; out obj): longint; stdcall; function _AddRef: longint; stdcall; function _Release: longint; stdcall;

I know from compiling and the pdf docs.

Everybody I know uses COM interfaces, except for one developers - Martin from MSEgui project. FPC defaults to COM style interfaces as well.

I think that's a matter of the environment. I switched to CORBA too, because I wanted to get up a testing application fast. ;)

But CORBA isn't that widely used, I think. Dunno what will happen on Windows when using corba for interfaces inside one application only, but I'm not sure if I can avoid using COM. At least driving some other apps like Winword or OO.o might come into play, but no export.

Marc

Martin Schreiber
to Marc Santhoff: Okay, so there are still some unknowns. However, before something goes into production…
Marc Santhoff wrote 22 lines
Am Sonntag, den 18.05.2008, 23:41 +0200 schrieb Graeme Geldenhuys: > 2008/5/18 Marc Santhoff : > > Since the compiler dod not warn about using the "as" operator I assume > > it is safe? Like this: > > > > procedure TObservable.DeRegister( obsv: IObserver ); > > var > > i: integer; > > begin > > if (fObservers.count<=0) then exit; > > for i:=0 to fObservers.count-1 do begin > > if ((fObservers[i] as IObserver)=obsv) then begin > > I'm not a big Interfaces expert, but as for as I know using 'as' like > that with Interfaces actually translates to a QueryInterface() call > internally, which is why it is allowed. I vaguely remember as issue > between CORBA and COM interfaces and the 'as' call, because CORBA > interfaces do not have a QueryInterface() implementation. Well, > something like that... :-) Okay, so there are still some unknowns.

TObject.GetInterface does not work with corba style interfaces: http://bugs.freepascal.org/view.php?id=5800
http://bugs.freepascal.org/view.php?id=6798
http://bugs.freepascal.org/view.php?id=6036

For a workaround use "getcorbainterface" in lib/common/kernel/mseclasses.pas from MSEide+MSEgui:

http://sourceforge.net/projects/mseide-msegui/

Martin