casting interfaces and objects

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

casting interfaces and objects

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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Graeme Geldenhuys-2
2008/5/18 Marc Santhoff <[hidden email]>:
> 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/
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Joao Morais
In reply to this post by Marc Santhoff
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.

> 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

> 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  -  [hidden email]
> http://lists.freepascal.org/mailman/listinfo/fpc-pascal
>

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Marco van de Voort
In reply to this post by Marc Santhoff
> 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



_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Marc Santhoff
In reply to this post by Joao Morais
Am Sonntag, den 18.05.2008, 17:54 -0300 schrieb Joao Morais:

> 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.

> > 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.

> > 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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Marc Santhoff
In reply to this post by Graeme Geldenhuys-2
Am Sonntag, den 18.05.2008, 22:51 +0200 schrieb Graeme Geldenhuys:

> 2008/5/18 Marc Santhoff <[hidden email]>:
> > 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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Marc Santhoff
In reply to this post by Marco van de Voort
Am Sonntag, den 18.05.2008, 23:07 +0200 schrieb Marco van de Voort:

> > 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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Marco van de Voort
> 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.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Graeme Geldenhuys-2
In reply to this post by Marc Santhoff
2008/5/18 Marc Santhoff <[hidden email]>:

> 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/
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Marc Santhoff
Am Sonntag, den 18.05.2008, 23:41 +0200 schrieb Graeme Geldenhuys:

> 2008/5/18 Marc Santhoff <[hidden email]>:
> > 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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Graeme Geldenhuys-2
Marc Santhoff wrote:

> Am Sonntag, den 18.05.2008, 23:41 +0200 schrieb Graeme Geldenhuys:
>> 2008/5/18 Marc Santhoff <[hidden email]>:
>>> 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/


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Marc Santhoff
Am Montag, den 19.05.2008, 09:16 +0200 schrieb Graeme Geldenhuys:

> Marc Santhoff wrote:
> > Am Sonntag, den 18.05.2008, 23:41 +0200 schrieb Graeme Geldenhuys:
> >> 2008/5/18 Marc Santhoff <[hidden email]>:
> >>> 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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: casting interfaces and objects

Martin Schreiber
In reply to this post by Marc Santhoff
On Monday 19 May 2008 00.02:55 Marc Santhoff wrote:

> Am Sonntag, den 18.05.2008, 23:41 +0200 schrieb Graeme Geldenhuys:
> > 2008/5/18 Marc Santhoff <[hidden email]>:
> > > 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
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal