Applying C Templates to COM

[Previous] [Next]

The examples we've covered in this chapter were inspired by ATL. We'll investigate how templates are useful to COM developers as we proceed through the book. For now, however, we'll present a couple of highlights.

One way ATL uses templates is to parameterize its implementations of IUnknown. IUnknown is the single interface that every COM class must implement. IUnknown describes two areas of functionality: interface type coercion (QueryInterface) and object lifetime control (AddRef and Release). AddRef and Release might work differently depending on what threading model is used. For example, if an object is meant to run in a single-threaded apartment, AddRef and Release might be implemented using the more efficient increment (++) and decrement (--) operators. An object meant to run within the multithreaded apartment should use the thread-safe (and less efficient) InterlockedIncrement and InterlockedDecrement functions. ATL provides two template classes, CComSingleThreadModel and CComMultiThreadModel, for specializing the reference-counting behavior of an ATL-based COM class. ATL's base class for implementing AddRef and Release is CComObjectRootEx. As a template class, CComObjectRootEx takes a class with member functions for references counting as a template parameter. Here is ATL's core class for implementing IUnknown::AddRef and IUnknown::Release:

 template <class ThreadModel> class CComObjectRootEx : public CComObjectRootBase {     typedef ThreadModel _ThreadModel; public:     ULONG InternalAddRef() {         return _ThreadModel::Increment(&m_dwRef);     }     ULONG InternalRelease() {         return _ThreadModel::Decrement(&m_dwRef);     } }; 

Notice how the CComObjectRootEx class maintains a variable of the ThreadModel type passed in as the template parameter. CComObjectRootEx's AddRef and Release implementations simply delegate to the template parameter.

Another way in which templates are useful to COM developers is within implementations of IClassFactory::CreateInstance. What's the difference between one implementation of IClassFactory and another? Very little, except for the type of C++ object produced during the invocation of CreateInstance. Coding a class that can return different types of objects is a perfect use for a C++ template. For example, you might write a generic templatized version of IClassFactory like so:

 template <class T> class CoGenericClassFactory : IClassFactory { public:     // Other members were deleted for clarity.     STDMETHODIMP CreateInstance(IUnknown* pUnk,                                 REFIID riid,                                  void** ppv) {         *ppv = 0;         if (pUnkOuter != 0)              return CLASS_E_NOAGGREGATION;         T* pT = new T;         if (!pT)              return E_OUTOFMEMORY;         pT->AddRef( );           HRESULT hr = pT->QueryInterface(riid, ppv);         pT->Release( );              return hr;     }     // Other members were deleted for clarity. }; 

The code is parameterized so that the new operator creates the type of class factory for a particular COM-based C++ class. Whenever a client calls CoCreateInstance, control flow ends up in IClassFactory, which creates the right kind of class.

Finally, ATL does have its own version of smart pointers, named CComPtr and CComQIPtr. They work much like the smart pointer we looked at earlier in this chapter. They hold interface pointers as data members. They overload the ->, *, and & operators, allowing clients to treat them as pointers. Because these are COM pointers, they call Release during their constructors. In addition, their assignment operators are overloaded to call Release on the old pointer data member and AddRef on the new assignee.

Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127 © 2008-2017.
If you may any questions please contact us: