In a header, following code is included.
inline void SafeRelease( IUnknown * & in_COM_Pointer )
{
if ( NULL != in_COM_Pointer )
{
in_COM_Pointer->Release();
in_COM_Pointer = NULL;
}
}
When It is used as given below,
SafeRelease(_D3DDevice); // _D3DDevice is a "IDirect3DDevice9 *". According to documentation it is inherited from "IUnknown".
It gives a compilation error:
error C2664: 'SafeRelease' : cannot convert parameter 1 from 'IDirect3DDevice9 *' to 'IUnknown *&'
I know how to write this function using templates or macros. But I want to know why this happens.
” 1. Why does this give error?
Consider this:
struct Animal {};
struct Dog: Animal { void bark() {} };
struct Dolphin: Animal { void dive() {} };
void foo( Animal*& p ) { p = new Dolphin(); }
auto main() -> int
{
Dog* p = new Dog();
foo( p ); //! C2664, Would have changed p to point to Dolphin.
p->bark(); // Uh huh...
}
So, this is not permitted.
There’s more of the same, e.g. regarding deep const
-ness of actual versus formal argument, and it’s generally known as the Liskov Substitution Principle, the LSP, after Barbara Liskov.
” 2. How to write it correctly?
One solution for the general problem, as Hans Passant has already mentioned, is to use templating in order to deal directly with the type or types at hand, no conversion.
In this concrete case, however, as long as you’re sure that you don’t have a nullpointer, just call p->Release()
instead of SafeRelease( p )
.
” 3. If inheritance cannot be used for writing this function should I use templates?
You can use templating but it’s not necessary; see above.
” 4. Anything that I would have to be careful about when implementing it using suggested method?
The suggested method involves an envisioned implicit conversion Derived*
→ Base*
for COM interface pointers.
Do note that while Derived*
→ Base*
conversion generally works nicely also with COM interfaces, the IUnknown
interface is subject to very special rules.
Namely, internally a COM object may have multiple IUnknown
sub-objects, corresponding to possible IUnknown*
pointers into this object, but only one of these pointer values identifies the object.
So when you want an IUnknown
pointer that identifies the object, a pointer that can be compared to other IUnknown
pointers to check if it’s the same object, you have to use QueryInterface
to obtain the IUnknown
pointer.
Happily you can use QueryInterface
via any interface pointer that you have, and since this member function is provided via the IUnknown
interface that all other interfaces inherit, it illustrates that you can use non-identifying IUnknown
pointers for other purposes than identification.