Collection Objects in ATL

[Previous] [Next]

ATL provides limited assistance in building collection objects through the ICollectionOnSTLImpl class. To create a collection object, ICollectionOnSTLImpl serves as a base class to your collection object. You can start with a simple object generated by the Object Wizard and add inheritance from ICollectionOnSTLImpl, which implements the Count property, the Item property, and the _NewEnum property on a Standard Template Library (STL) sequential container such as a vector or a list. If you need to keep your collection in a map and also support retrieving items by string value, you're probably better off building your own collection implementation from scratch.

You'll need to add the matching IDL for the ICollectionOnSTLImpl properties yourself. The text to add to your collection object interface IDL is shown here in bold, inserted in our analog Inputs collection dual interface:

 interface IInputs : IDispatch {     [propget, id(DISPID_NEWENUM)] HRESULT _NewEnum([out,retval]         LPUNKNOWN* pUnk);     [propget, id(DISPID_VALUE)] HRESULT Item([in] long Index,         [out,retval] VARIANT* pvar);     [propget, id(1)] HRESULT Count([out,retval] long* pcount); }; 

ATL defines Item as a property, not a method. You'll see implementations that declare Item and other collection members both ways, depending on how the designer interpreted the spec. Notice that there are no optional Add or Remove methods. If you need them, you'll have to add their implementations and IDL to your collection object by hand.

ICollectionOnSTLImpl derives from an interface you supply—typically your collection object's primary dual interface. The definition of a hypothetical collection class that models analog input on a circuit board might look like this:

 class ATL_NO_VTABLE CInputs :      public CComObjectRootEx<CComSingleThreadModel>,     public CComCoClass<CInputs, &CLSID_Inputs>,     public ICollectionOnSTLImpl<lots of parameters> {  } 

The template parameters to ICollectionOnSTLImpl can get a little messy, so we'll look at them one at a time. The template definition for the ATL ICollectionOnSTLImpl class is shown here:

 template <class T, class CollType, class ItemType,     class CopyItem, class EnumType> class ICollectionOnSTLImpl : public T {   } 

The remaining template parameters for this code are described in the following sections.

class T

This class is the base class for ICollectionOnSTLImpl. For our analog Inputs collection, which supports a dual interface IInputs, the base class type is defined as follows:

 IDispatchImpl<IInputs, &IID_IInputs, &LIBID_COLLECTIONSERVERLib> 

class CollType

CollType is the STL collection type that contains the items in the collection. The implementation supports sequential containers such as vectors and lists. Associative containers, such as maps, aren't supported in the current ICollectionOnSTLImpl class. Our analog Inputs collection uses an STL vector of IInput interface pointers, so the CollType parameter is defined as follows:


ICollectionOnSTLImpl maintains the STL collection object for you in its m_coll data member. You can access m_coll directly to populate the collection with initial items. The analog Inputs collection object does this by creating a fixed number of analog input objects and adding them to the STL vector in the constructor, as shown in the following code section:

 enum {numInputs = 8}; CInputs() {     IInput* pIInput = NULL;     for (int i = 0; i < numInputs; i++)     {         CComObject<CInput> *pInput = new CComObject<CInput>;         if(SUCCEEDED(pInput->QueryInterface(IID_IInput,             (void**)&pIInput)))             m_coll.push_back(pIInput);     } } 

The code adds eight analog input objects to the m_coll STL container. The destructor must iterate over m_coll and release the interface pointers, as illustrated here:

 ~CInputs() {     std::vector<IInput*>::iterator it;     for (it = m_coll.begin(); it != m_coll.end(); it++)     {         IInput* pIInput = *it;         if(pIInput)             pIInput->Release();     } } 

class ItemType

This parameter describes the type of item contained in the collection and defines the return type for the Item property of the collections. Our analog Inputs collection object uses a VARIANT for the ItemType parameter. Clients call the Item property to retrieve, by numeric index, a single value from the collection. The following code for ICollectionOnSTLImpl::get_Item illustrates the use of the m_coll STL container to retrieve a collection item of type ItemType:

 STDMETHOD(get_Item)(long Index, ItemType* pvar) {     // Index is 1-based     if(pvar == NULL)         return E_POINTER;     HRESULT hr = E_FAIL;     Index--;     CollType::iterator iter = m_coll.begin();     while (iter != m_coll.end() && Index > 0)     {         iter++;         Index--;     }     if(iter != m_coll.end())         hr = CopyItem::copy(pvar, &*iter);     return hr; } 

The returned value is of type ItemType*, which is assigned in the CopyItem::copy call. CopyItem is also specified in a template parameter, which we'll look at next.

class CopyItem

ATL collection and enumerator classes use a helper class to copy a collection item from the STL container to the return value of the Item property. The behavior implemented by the CopyItem class is also known as a copy policy. The class implementing the copy policy must have a static member with this signature:

 HRESULT copy(/*[out]*/ ItemType* p1, /*[in]*/ STLItemType* p2) 

The copy policy takes as input the result of dereferencing the STL iterator to retrieve an item and then taking its address, as we see in the following code:

 if(iter != m_coll.end())     hr = CopyItem::copy(pvar, &*iter); 

If the STL container is holding IInput interface pointers, the copy policy gets an IInput** parameter for p2. The parameter p1 is an [out] parameter of the type that the get_Item property returns to the client. Typically this is a VARIANT VT_DISPATCH for object collections, so p2 is a VARIANT* parameter. Therefore, the purpose of our copy policy is to copy an interface pointer to a VARIANT dispatch pointer. A copy policy class that implements this functionality is shown here:

 class _CopyVariantFromInterface { public:     static HRESULT copy(VARIANT* pV, IUnknown** pUnk)     {         HRESULT hr = (*pUnk)->QueryInterface(IID_IDispatch,             (void**)&pV->pdispVal);         if(SUCCEEDED(hr) )             pV->vt = VT_DISPATCH;         return hr;     } }; 

We can use the type _CopyVariantFromInterface as the CopyItem template parameter to ICollectionOnSTLImpl for the analog Inputs collection or for any collection that returns a dispatch-based VARIANT from the Item property.

class EnumType

The _NewEnum property of a collection creates and initializes an enumerator object. The client receives the IUnknown pointer for the object as the return value to _NewEnum. The code section that follows shows how ICollectionOnSTLImpl defines get_ _NewEnum:

 STDMETHOD(get_ _NewEnum)(IUnknown** ppUnk) {     if (ppUnk == NULL)         return E_POINTER;     *ppUnk = NULL;     HRESULT hRes = S_OK;     CComObject<EnumType>* p;     hRes = CComObject<EnumType>::CreateInstance(&p);     if(SUCCEEDED(hRes))     {         hRes = p->Init(this, m_coll);         if(hRes == S_OK)             hRes = p->QueryInterface(IID_IUnknown, (void**)ppUnk);     }     if(hRes != S_OK)         delete p;     return hRes; } 

The method creates an enumerator object using CComObject<EnumType>. The enumerator is then initialized by passing it a reference to the collection's STL container, m_coll. The specification for EnumType in the analog Inputs collection is shown here:

 CComEnumOnSTL<CComIEnum<VARIANT>, &IID_IEnumVARIANT,     VARIANT, _CopyVariantFromInterface<IInput>,     std::vector<IInput*> > 

Code like this can cause technical support engineers to come looking for you. A typedef is highly recommended. ATL provides the CComEnumOnSTL enumerator implementation for use as the EnumType in ICollectionOnSTLImpl. We'll give CComEnumOnSTL plenty of attention at the end of the following section on enumerator objects.

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: