- let we have
I1
interface andI2
inherited fromI1
(struct I2 : public I1 {..}
in c++ notation) - let we have pointer to
I2
interface:I2* p2;
- we need get pointer to
I1
interface.
question:
are
I1* p1 = p2;
// use p1
always valid by semantic without any assumption, implementations details etc.
and we not need call QueryInterface
here (ie p2->QueryInterface(IID_PPV_ARGS(&p1)
)
(from c++ syntax we even not need use explicit cast in I1* p1 = p2;
here, in c need cast I1* p1 = (I1*)p2;
but this is not language layer question)
or even more concrete example. say some function require pointer to I1
interface ( void fn(I1*)
), but we have pointer to I2* p2
.
can we call/pass fn(p2)
as is ( from c++ formal rules this is correct - we can pass pointer to derived class in place base pointer)
my opinion that yes, and this is obvious, but because exist another opinion - I just wonder to hear the arguments
so why is this cast ok?
- we can call any method from
I2
by usingp2
pointer? yes - any method of
I1
was method ofI2
too ? yes, becauseI2
inherit fromI1
- from
1
and2
we can call any method ofI1
by usingp2
pointer
and this is enough. here no any assumption at all.
by using p2
pointer as is (without any binary modification) we can call any methods of I1
interface.
as result this is valid pointer to I1
interface too
possible and say next
I1* p1 = p2; // p1 == p2 on binary level, because COM interface have single vtable pointer
p1->func(..);// let func is some method of I1 interface
p2->func(..);// will be the exactly same binary code as p1->func(..)
so p1->func(..)
correct when and only when, p2->func(..)
is correct. but p2->func(..)
is correct
can somebody say what's wrong here? are exist hidden assumption? are possible provide example when this is wrong?
some additional notes
note#1:
please not confuse caste I2
to I1
with cast I1
to I2
. this cast is wrong and require call to QueryInterface
I1* p1;
I2* p2;
p2 = static_cast<I2*>(p1); // wrong !!
p1->QueryInterface(IID_PPV_ARGS(&p2)) // ok
why? let exist interface I3
which is also inherit from I1
, but I3
not inherit from I2
and I2
not inherit from I3
and some object implement both I2
and I3
(as result and I1
)
this mean that object containing how minimum 2 different vtable pointers - one to I2
and one to I3
when we query to I1
interface, QueryInterface
can return
static_cast<I1*>(static_cast<I2*>(this)) // pointer to I1 via I2 vtable
but can and return
static_cast<I1*>(static_cast<I3*>(this)) // pointer to I1 via I3 vtable
if p1
point I2
vtable cast will be ok, but if p1
point to I3
vtable - will be wrong result - p1
not valid pointer to I2
in this case
but again - question about another conversion. so all this unrelated to question.
note#2
but after p2->QueryInterface(IID_PPV_ARGS(&p1))
we can got another binary pointer p1
( p1 != p2
on binary level) ?
yes, can! the same as in previous note - if object inherit I2
and I3
(both it inherit from I1
but not from each other)
in result of p2->QueryInterface(IID_PPV_ARGS(&p1))
call can be returned or
static_cast<I1*>(static_cast<I2*>(this)) // in this case will be p2 == p1
or
static_cast<I1*>(static_cast<I3*>(this)) // in this case will be p2 != p1
so if QueryInterface
can return different binary pointer to I1
(not equal to original p2
) - which one is correct? this say about p1 = p2
; cast wrong ?
no. the both pointers is correct and can be used for call methods of I1
interface.
many may be say here - wait, but how this can be?
if we call
p1->func(..)
and p2->func(..)
and p1 != p2
the func()
will be called with different pointers, but only one can be correct?
error here - that we have 2 different func()
- in case p1 != p2
the p1->func
can be != p2->func
func
here not concrete function address. but this address taken from vtable to which point p1
or p2
.
because p1 != p2
- here different vtables will be used and different pointers in this vtables.
how minimum in one of this vtables will be adjustor thunk (possible in both) which adjust incoming p1
(or p2
) pointer and then jump to "real" func
implementation
so again - both pointers (p1 = p2
and p2->QueryInterface(IID_PPV_ARGS(&p1))
) is correct here and both can be used
note#3:
please not confuse COM Interface and object which implement one or more com interfaces. from Interface Pointers and Interfaces
An instance of an interface implementation is actually a pointer to an array of pointers to methods - that is, a function table that refers to an implementation of all of the methods specified in the interface.
also look What Is a COM Interface?
i not ask about convert pointer to object which implement (inherit) interface to pointer to this interface. i ask only about interface pointer conversion.
interface from c/c++ declaration - this is pointer size object - containing single pointer to array of pointers to methods
interface IXxx
{
CONST_VTBL struct IXxxVtbl *lpVtbl;
};
for instance
class I3 : public I1, I2
{
// new methods
};
here I3
not interface, but object which implement I1
and I2
interfaces.
note#4 "layout of interface is unknown" - this is not true. layout of interface is always known and explicit defined. layout of object, which implement interface is unknown. but this is different things - i not assume any layout of object. again why example with
class I3 : public I1, I2
{
// new methods
};
is wrong.
here binary pointer to I3
not valid pointer to I2
(require adjustment) despite I3
inherit from I2
.
but com interface can not be such defined. i say next
if com interface I2
inherit from I1
interface - pointer to I2
is always valid pointer to I1
too. this is from interface definition
can only again repeat example
I1* p1 = p2;
p1->func(..);
p2->func(..);
p1
will be the same binary value as p2
here - this is due specific interfaces definitions. as result p1->func(..);
and p2->func(..);
produce the same binary code. if p1->func(..);
will be wrong - p2->func(..);
will be wrong too. but p2->func(..);
is correct by definiton