IQueryGuid and guid_cast
One way to create functionality similar to
QueryInterface() is to use the C++
dynamic_cast() operator. This is an effective solution if your compiler supports it. Enabling RTTI also introduces some extra overhead for every class compiled. Many developers prefer to avoid enabling RTTI, or at least want a choice in the matter. The solution used by SFL avoids the use of C++ RTTI by introducing an interface that provides a function very similar to
IUnknown’s
QueryInterface(). The
IQueryGuid interface has a single method,
QueryGuid() which allows the caller to pass in a GUID and get back a pointer to an interface or class. It is exactly like
QueryInterface(), except that
QueryGuid() is more generic.
QueryInterface() is only meant to get back pointers to interfaces.
QueryGuid() acts as a substitute for
dynamic_cast, so it is perfectly acceptable to associate a GUID with a concrete class and then use
QueryGuid() to cast pointers to that concrete class.
Example 1 shows how
IQueryGuid is defined.
Example 1 – Defining IQueryGuid
class IQueryGuid
{
public:
virtual bool QueryGuid(REFGUID guid,void **ppvObj)=0;
};
QueryGuid() is similar to QueryInterface() with a couple of notable exceptions. First, QueryGuid() returns TRUE if the interface is supported by the object and FALSE if it fails. The most important difference is that QueryGuid() does not make any assumptions about reference counting. Although QueryInterface() always increments the reference count on an interface before returning it to the caller, QueryGuid() does not. This is because IQueryGuid does not have any reference counting methods. It is perfectly valid to use IQueryGuid for casting interfaces and classes that do not support reference counting.
Example 2 shows a class that implements
IQueryGuid.
Example 2 – Implementing IQueryGuid
class __declspec(uuid("81CEDD2C-B2F0-4702-AA2F-D912497F5F33"))
IAnimal : public IQueryGuid
{
public:
virtual void Eat() = 0;
virtual void Sleep() = 0;
virtual void Reproduce() = 0;
};
class CCow : public IAnimal
{
public:
virtual bool QueryGuid(REFGUID guid,void **ppvObj)
{
*ppvObj = NULL;
if (guid == __uuidof(IAnimal))
*ppvObj = static_cast<IAnimal*>(this);
else if (guid == __uuidof(IQueryGuid))
*ppvObj = static_cast<IQueryGuid*>(this);
return (*ppvObj != NULL);
}
virtual void Eat()
{
// chew some grass
}
virtual void Sleep()
{
// sleep standing up?
}
virtual void Reproduce()
{
// not a pretty sight
}
};
NOTE >> Notice that declspec uuid is used to associate a GUID with the IAnimal interface. The __uuidof operator can then be used to return the GUID for IAnimal in the implementation of QueryGuid().
The
guid_cast template function makes
QueryGuid() type safe, so it is more like
dynamic_cast and easier to use. It acts like
dynamic_cast and is implemented by calling
QueryGuid().
Example 3 shows how
guid_cast is used.
Example 3 – Using the guid_cast template function
void MakeAnimalEat(IQueryGuid* pObj)
{
IAnimal* pAnimal = guid_cast<IAnimal*>(pObj);
if (pAnimal)
pAnimal->Eat();
}