Board index » delphi » why interface casting can burn in...

why interface casting can burn in...

I just have to rant a bit here on a problem that had me pulling my hair out
for the last 5 hours.

I had very simple design, something like this:

IDocument = interface;
IDocumentTemplate = interface(IDocument);

Then I used everyone's favorite IInterfaceList to keep track of all
documents, regardless of type (IDocument or IDocumentTemplate).  What had me
fuming was perhaps my stupid assumption about the following code:

var
    GlobalList: IInterfaceList;

procedure TForm1.RiddleMeThis;
var
    doc: IDocument;
    index: integer;
begin
    doc := CoDocumentTemplate.create;

    // add interface to list
    GlobalList.Add( doc );

    // get the first document in the list
   doc := GlobalList.First as IDocument;

    // get the index of the first object(obviously this is
    // dumb since I just did GlobalList.First but this illustrates the
problem)
    //
   index := GlobalList.IndexOf( doc );
   if index >= 0 then
    showmessage('gotcha');
end;

From the above code I'd expect that the variable 'index' would be set to 0
but instead it is set to -1.  Works just fine if I only use the base
IDocument class but mixing any descendant classes throws it all off.

The resolution is to be highly redundent.  When adding the variable 'doc' to
the interfacelist you must manually cast it down to the base class.  So on
my IDocument variable I need to do the following:

    GlobalList.Add( doc as IDocument );

Anyone out there tell me why I have to cast my variable as it's own type
just to get the pointer addresses to match up?  Am I missing something
obvious here?  doc is of type IDocument, why do I need to recast it as
itself?

Simian

 

Re:why interface casting can burn in...


Quote
"Simian Jones" <simianjo...@hotmail.com> wrote in message news:3c76bbe6$1_2@dnews...
> var
>     GlobalList: IInterfaceList;

> procedure TForm1.RiddleMeThis;
> var
>     doc: IDocument;
>     index: integer;
> begin
>     doc := CoDocumentTemplate.create;

>     // add interface to list
>     GlobalList.Add( doc );

>     // get the first document in the list
>    doc := GlobalList.First as IDocument;

>     // get the index of the first object(obviously this is
>     // dumb since I just did GlobalList.First but this illustrates the
> problem)
>     //
>    index := GlobalList.IndexOf( doc );
>    if index >= 0 then
>     showmessage('gotcha');
> end;

> From the above code I'd expect that the variable 'index' would be set to 0
> but instead it is set to -1.  Works just fine if I only use the base
> IDocument class but mixing any descendant classes throws it all off.

An object that supports multiple interfaces will also return
different interface pointers for each (since they refer to different
virtual method tables). So, If you put an item into the list as IDocument,
you have to search for it as IDocument.

Quote
> The resolution is to be highly redundent.  When adding the variable 'doc' to
> the interfacelist you must manually cast it down to the base class.  So on
> my IDocument variable I need to do the following:

>     GlobalList.Add( doc as IDocument );

> Anyone out there tell me why I have to cast my variable as it's own type
> just to get the pointer addresses to match up?  Am I missing something
> obvious here?  doc is of type IDocument, why do I need to recast it as
> itself?

To be precise, you need to cast it to the same interface that you
used to put it into the interface list. So, it is wise to pick one
interface (obviously the base interface) and stick with it for
all operations on one list.

Note: Since every interface (e.g. IDocument in this case) derives from
IUnknown, you can cast it like this "IUnknown(Doc)". However, this will
not return the same pointer value as "Doc as IUnknown".
The latter will return the basic IUnknown, as implemented by
TInterfacedObject, for instance.

Karl

Re:why interface casting can burn in...


Quote
"Karl Waclawek" <k...@waclawek.net> wrote in message

news:3c773576_2@dnews...

Quote

> > From the above code I'd expect that the variable 'index' would be set to
0
> > but instead it is set to -1.  Works just fine if I only use the base
> > IDocument class but mixing any descendant classes throws it all off.

> An object that supports multiple interfaces will also return
> different interface pointers for each (since they refer to different
> virtual method tables). So, If you put an item into the list as IDocument,
> you have to search for it as IDocument.

That is exactly what I did though.  My variable was of type IDocument, the
base class, not IDocumentTemplate.  Take the following code:

var
    doc: IDocument;
begin
    doc := CoDocumentTemplate.create;
end;

doc is of the base type.  Why do I need to cast it yet again to IDocument?
Your explination makes perfect sense if I had declared the variable of some
descendant class but I didn't.

Quote

> To be precise, you need to cast it to the same interface that you
> used to put it into the interface list. So, it is wise to pick one
> interface (obviously the base interface) and stick with it for
> all operations on one list.

Isn't that what I did in the example above?  It still doesn't work unless I
redundently write (doc as IDocument) when adding it into the list.

I figured out something that I think is perhaps a bug or else my idea of COM
is way off.

if you have this...

IDoc = interface
IDocEx = interface(IDoc)

TDoc = class(TInterfacedObject, IDoc);
TDocEx=class(TDoc, IDoc, IDocEx);

Then you get the problem I am talking about and you have to do this weird
redundent 'as' casting to get it to add correctly.  BUT... if you declare it
as...

TDoc = class(TInterfacedObject, IDoc);
TDocEx=class(TDoc, IDocEx);  // no IDoc

Then it works perfectly.  Of course now you have to cast the interface up
and down to get to the different properties.  Stupid interfaces.

Re:why interface casting can burn in...


Quote
"Simian Jones" <simianjo...@hotmail.com> wrote in message news:3c7c2855$1_2@dnews...

> "Karl Waclawek" <k...@waclawek.net> wrote in message
> news:3c773576_2@dnews...
> > An object that supports multiple interfaces will also return
> > different interface pointers for each (since they refer to different
> > virtual method tables). So, If you put an item into the list as IDocument,
> > you have to search for it as IDocument.

> That is exactly what I did though.  My variable was of type IDocument, the
> base class, not IDocumentTemplate.  Take the following code:

> var
>     doc: IDocument;
> begin
>     doc := CoDocumentTemplate.create;
> end;

> doc is of the base type.  Why do I need to cast it yet again to IDocument?
> Your explination makes perfect sense if I had declared the variable of some
> descendant class but I didn't.

What matters is what CoDocumentTemplate returns.
If it returns IDocumentTemplate, then this is a sub type of
IDocument and can therefore be assigned to doc.
However, if you had done doc := CoDocumentTemplate.create as IDocument;
you would have gotten the explicit IDocument interface.
These are two different pointers!

Quote
> > To be precise, you need to cast it to the same interface that you
> > used to put it into the interface list. So, it is wise to pick one
> > interface (obviously the base interface) and stick with it for
> > all operations on one list.

> Isn't that what I did in the example above?  It still doesn't work unless I
> redundently write (doc as IDocument) when adding it into the list.

Yes, because you need the IDocument pointer, not the IDocumentTemplate pointer.

- Show quoted text -

Quote
> I figured out something that I think is perhaps a bug or else my idea of COM
> is way off.

> if you have this...

> IDoc = interface
> IDocEx = interface(IDoc)

> TDoc = class(TInterfacedObject, IDoc);
> TDocEx=class(TDoc, IDoc, IDocEx);

> Then you get the problem I am talking about and you have to do this weird
> redundent 'as' casting to get it to add correctly.  BUT... if you declare it
> as...

> TDoc = class(TInterfacedObject, IDoc);
> TDocEx=class(TDoc, IDocEx);  // no IDoc

> Then it works perfectly.  Of course now you have to cast the interface up
> and down to get to the different properties.  Stupid interfaces.

TDocEx=class(TDoc, IDoc, IDocEx); means that you are re-implementing
the interface IDoc. But show me the concrete example with those
declarations that gives you a problem with one and not the other.

Karl

Other Threads