Why exactly do I need an explicit upcast when implementing QueryInterface() in an object with multiple interfaces()
The problem is that *ppv
is usually a void*
- directly assigning this
to it will simply take the existing this
pointer and give *ppv
the value of it (since all pointers can be cast to void*
).
This is not a problem with single inheritance because with single inheritance the base pointer is always the same for all classes (because the vtable is just extended for the derived classes).
However - for multiple inheritance you actually end up with multiple base pointers, depending on which 'view' of the class you're talking about! The reason for this is that with multiple inheritance you can't just extend the vtable - you need multiple vtables depending on which branch you're talking about.
So you need to cast the this
pointer to make sure that the compiler puts the correct base pointer (for the correct vtable) into *ppv
.
Here's an example of single inheritance:
class A {
virtual void fa0();
virtual void fa1();
int a0;
};
class B : public A {
virtual void fb0();
virtual void fb1();
int b0;
};
vtable for A:
[0] fa0
[1] fa1
vtable for B:
[0] fa0
[1] fa1
[2] fb0
[3] fb1
Note that if you have the B
vtable and you treat it like an A
vtable it just works - the offsets for the members of A
are exactly what you would expect.
Here's an example using multiple inheritance (using definitions of A
and B
from above) (note: just an example - implementations may vary):
class C {
virtual void fc0();
virtual void fc1();
int c0;
};
class D : public B, public C {
virtual void fd0();
virtual void fd1();
int d0;
};
vtable for C:
[0] fc0
[1] fc1
vtable for D:
@A:
[0] fa0
[1] fa1
[2] fb0
[3] fb1
[4] fd0
[5] fd1
@C:
[0] fc0
[1] fc1
[2] fd0
[3] fd1
And the actual memory layout for D
:
[0] @A vtable
[1] a0
[2] b0
[3] @C vtable
[4] c0
[5] d0
Note that if you treat a D
vtable as an A
it will work (this is coincidence - you can't rely on it). However - if you treat a D
vtable as a C
when you call c0
(which the compiler expects in slot 0 of the vtable) you'll suddenly be calling a0
!
When you call c0
on a D
what the compiler does is it actually passes a fake this
pointer which has a vtable which looks the way it should for a C
.
So when you call a C
function on D
it needs to adjust the vtable to point to the middle of the D
object (at the @C
vtable) before calling the function.
When implementing several COM interfaces at once how do I upcast to IUnknown?
Mark Ransom already gave the correct answer - any will do, as long as it's consistent - but picking the first one has one minor advantage. Due to layout rules, the IUnknown*
of the first interface will point to the start of the object. Any other IUnknown*
will point to subsequent vtable pointers elsewhere in the object. For debugging purposes, it's very useful to know where ano object begins in memory.
Is using implicit conversion for an upcast instead of QueryInterface() legal with multiple inheritance?
COM has no rules regarding interface identity, only of object identity. The first rule of QI says that a QI on IID_Unknown on two interface pointers must return the same pointer if they are implemented by the same object. Your QI implementation does this correctly.
Without a guarantee for interface identity, a COM method cannot assume that it gets the same IUnknown pointer passed that it will retrieve when it calls QI on that pointer. So if object identity needs to be proven then a separate QI is required.
Is there any reason against directly calling AddRef() inside QueryInterface() implementation?
AddRef
is pure virtual in IUnknown
, and none of the other interfaces implement it, so the only implementation in your program is the one you write in CMyClass
. That one method overrides both IInterface1::AddRef
and IInterface2::AddRef
. IUnknown
doesn't have any data members (such as a reference count), so the diamond problem doesn't cause your class to be susceptible to a problem such as different calls to AddRef
acting on different data in the same class.
Calls to this->AddRef()
are going to be routed to the same place as static_cast<IUnknown*>(*ppv)->AddRef()
. I see no reason for the more verbose style.
How does implementing multiple COM interfaces work in C++?
Yes, there are multiple v-tables, one for each inherited interface. The static_cast<> returns it. The compiler makes sure that common methods in the inherited interfaces are shared, it fills each v-table slot with the a pointer to the same function. So you only need one implementation of AddRef, Release, QueryInterface. Just what you want. None of this is an accident.
This is only ever a problem when a coclass implements multiple interfaces with the same method that you don't want to give the same implementation. The IConnectionPoint::Advise() method is a notorious example. Or was it DAdvise()? Unfortunately, I don't remember what it clashed with and how it was solved, it was covered by ATL Internals. Very good book btw.
Good techniques for keeping COM classes that implement multiple interfaces manageable
Assuming that you don't need the handler objects to be separate C++ instances from the overall object, then the following may work...
If I Remember my COM Correctly... - why not just leave FooHandler as a partially abstract base class - leave the IUnknown portion unimplemented. Have MyCOMClass derive from all the necessary handlers; and then implement IUnknown only in that most-derived class. The AddRef/Release/QI you supply there will be used for all the base classes. (Typically can just forward AddRef/Release to the CUnknown class or some base that does the refcounting housekeeping, but likely need to implement QI manually since this is the only place that you know fully what set of interfaces you want to expose.)
In other words, keep doing what you are doing; but you don't need to do the delegation part manually: the compiler actually does similar for you behind the scenes anyhow due to how Multiple Inheritance of interfaces (specifically, classes with virtual methods) works in C++. The magic part is that methods declared in the most-derived interface by default override all those of the same name and parameter signature in the base classes; so any base class that calls AddRef or QI in their own IUnknown will end up actually calling the version you supply in the most-derived class.
Code likely looks something like:
class MyCOMClass
, public CUnknown // Assume this handles refcounting (and has a virtual dtor!)
, public CFooHandler // Implements IFoo
, public CBarHandler // Implements IBar
{
... add any interfaces that MyCOMClass itself is implementing...
// Actual impl of IUnknown...
STDMETHOD_(ULONG, AddRef)(); { return CUnknown::AddRef(); }
STDMETHOD_(ULONG, Release)(); { return CUnknown::Release(); }
STDMETHOD(QueryInterface)(IN REFIID riid, OUT void** ppv)
{
*ppv = NULL;
// IUnknown can require extra casting to pick out a specific IUnknown instance
// otherwise compiler will complain about an ambiguous cast. Any IUnknown will do,
// we know they're all the same implementation, so even casting to CFooHandler then IUnknown is fine here.
// Here am assuming that CUnknown implements IUnknown
if(riid == __uuidof(IUnknown))
*ppv = static_cast<IUnknown*>(static_cast<CUnknown*>(this));
else if(riid == __uuidof(IFoo))
*ppv = static_cast<IFoo*>(this);
else if(riid == __uuidof(IBar))
*ppv = static_cast<IBar*>(this);
else
return E_NOINTERFACE;
// Usually you call AddRef on the interface you are returning; but
// we know we're using the same ref count for the whole object, so this
// is appropriate for this specific implementation strategy.
AddRef();
}
As a for-what-it's-worth, if you do want to implement the handlers on separate objects, then you will need to do the delegation that you are proposing - it's essentially a form of aggregation. But you don't need to implement the interfaces on the MyCOMClass and write lots of forwarders: all MyCOMClass has to do is implement QI such that the returned value - whether the same 'this' object or some separate object - is properly cast to the requested interface. But if you don't need separate objects, the above technique should work fine.
Why do IUnknown* pointers retrieved through different interfaces of the same COM object have the same value?
That's so called "object identity" requirement that states that whenever you request IUnknown
from two objects you get distinct pointers if those are distinct objects and equal pointers if that's the same object.
Every QueryInterface()
implementation must fulfill this requirement. This is usually done by choosing which one IUnknown
to return and sticking to it:
HRESULT CA::QueryInterface( REFIID iid, void** ppv )
{
if( iid == __uuidof( IUnknown ) ) {
// Explicitly cast to one of base class subobjects.
// Doesn't matter which one is chosen - it just has to be
// the same base class subobject each time IUnknown is requested.
IUnknown* selected = static_cast<IX*>( this );
*ppv = selected;
AddRef();
return S_OK;
} else {
continue for other interfaces
}
}
Is there any case when dynamic_cast should be used in the QueryInterface implementation?
It would be necessary in those implementations of QueryInterface
where the "supported interface ids" aren't known. E.g. if you decide to implement QueryInterface
in a base class, and not override it for every derived class.
A case where this would happen is the situation where you have a lot of similar types, where "similar" means "implementing many of the same interfaces". I.e. you have object types Derived1
... DerivedN
which all implement some subset of Interface1
...InterfaceM
.
This could be the case for a game engine where game entities all implement a subset of IMoveable
, IScriptable
, IFactory
, IShoots
, IPlayerControlled
, IStealthy
, ISensor
, etcetera. Of course, by COM rules you must be able to call IFactory::QueryInterface
and get an IMovable*
if and only if the factory also implements IMovable
.
How are you going to implement all those QueryInterface
methods? It's easiest to insert an GameObject
base class between IUnknown
and IFactory
, and implement GameObject::QueryInterface
using dynamic_cast
checks. In this way, you need only one implementation, instead of one per interface of a concrete type.
C++ COM design. Composition vs multiple inheritance
Multiple inheritance is a very common way to do COM interfaces, so yes it's possible.
However QueryInterface must still cast the pointer for each interface. One interesting property of multiple inheritance is that the pointer may get adjusted for each class type - a pointer to IDispatch won't have the same value as a pointer to IDocHostUIHandler, even though they both point to the same object. Also make sure that a QueryInterface for IUnknown always returns the same pointer; since all the interfaces derive from IUnknown you'll get an ambiguous cast if you try just to cast directly to it, but that also means you can use any interface as an IUnknown, just pick the first one in the parent list.
Related Topics
How to Get Memory Usage Under Windows in C++
Simple Way to Unzip a .Zip File Using Zlib
How to Emit Cross-Thread Signal in Qt
When Is It Necessary to Use the Flag -Stdlib=Libstdc++
Why Isn't the [] Operator Const for Stl Maps
Error: Invalid Operands of Types 'Const Char [35]' and 'Const Char [2]' to Binary 'Operator+'
Structure of Arrays VS Array of Structures
Multiple Definition of Template Specialization When Using Different Objects
Outputting Date and Time in C++ Using Std::Chrono
How to Remove All the Occurrences of a Char in C++ String
Is There a Range Class in C++11 for Use with Range Based for Loops
Why Should I Use the "Using" Keyword to Access My Base Class Method
C++ Exception:Throwing Std::String
Std::Fstream Buffering VS Manual Buffering (Why 10X Gain with Manual Buffering)