previous page
next page

Collections

ICollectionOnSTLImpl

In addition to parameterized implementations of enumeration interfaces, ATL provides parameterized implementations of collection interfaces, assuming that you're willing to keep your data in a standard C++-like container. The implementation is provided by the ICollectionOnSTLImpl class:

template <class T, class CollType, class ItemType,
          class CopyItem, class EnumType>         
class ICollectionOnSTLImpl : public T {           
public:                                           
  STDMETHOD(get_Count)(long* pcount);             
  STDMETHOD(get_Item)(long Index, ItemType* pvar);
  STDMETHOD(get__NewEnum)(IUnknown** ppUnk);      
                                                  
  CollType m_coll;                                
};                                                

The ICollectionOnSTLImpl class provides an implementation of the three standard collection properties much like what I showed you earlier. The chief difference is that the container is managed for you in the m_coll member data of the ICollectionOnSTLImpl class. That means that you can't provide a copy of the data to the enumerators, but you can still use a collection that calculates on demand and you can still convert from a convenient type to the type required by the enumerator exposed from get__NewEnum. This is because, although you get to decide the type of the container in a template parameter, you're no longer implementing get__NewEnum.

The template parameters of ICollectionOnSTLImpl are as follows:

  • The T parameter indicates the base classFor example, IDispatchImpl-<IPrimeNumbers and &IID_IPrimeNumbers>. ICollectionOnSTLImpl provides the implementation of the standard three properties of this base class, but the deriving class is responsible for the rest.

  • The CollType parameter indicates the type of container to keepfor example, vector<long> or PrimesContainer.

  • The ItemType parameter indicates the type of data exposed from the iterator of the collectionfor example, long.

  • The CopyItem parameter indicates the type of the copy policy class. This copy policy is used only in the implementation of the get_Item method. The copy policy should be capable of copying from a container that holds items of type ItemType to a single [out] parameter of type ItemType. If you were managing a container of long numbers, the CopyItem type would be _Copy<long>.

  • The EnumType parameter indicates the type of the enumeration-implementation class. This enumeration must be capable of enumerating over a container just like CComEnumOnSTL. An example EnumType parameter is CCom-Enum-OnSTLImpl<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT>, vector<VARIANT> >.

ICollectionOnSTLImpl Usage

The best way to understand the ICollectionOnSTLImpl class is to see it in action. The first C++based implementation of the IPrimesCollection standard collection interface assumed that we wanted to manage a precalculated container of VARIANTs. This can be done using ICollectionOnSTLImpl:

// Needed for implementation of get_Item.
// Converts the storage type (VARIANT) to the item type (long).
struct _CopyLongFromVariant {
  static HRESULT copy(long* p1, VARIANT* p2) {
    if (p2->vt == VT_I4) {
      *p1 = p2->lVal;
      return S_OK;
    }
    else {
      VARIANT var;
      HRESULT hr = VariantChangeType(&var, p2, 0, VT_I4);
      if (SUCCEEDED(hr)) *p1 = var.lVal;
      return hr;
    }
  }

  static void init(long* p) { }
  static void destroy(long* p) { }
};

// Needed for implementation of IDispatch methods
typedef IDispatchImpl<IPrimeNumbers, &IID_IPrimeNumbers>
  IPrimeNumbersDualImpl;

// Needed for implementation of get__NewEnum method
typedef CComEnumOnSTL<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT,
  _Copy<VARIANT>, vector<VARIANT> > ComEnumVariantOnVector;

// Needed for implementation of standard collection methods
typedef ICollectionOnSTLImpl<IPrimeNumbersDualImpl,
  vector<VARIANT>, long, _CopyLongFromVariant,
    CComEnumVariantOnVector>
    IPrimeNumbersCollImpl;

class ATL_NO_VTABLE CPrimeNumbers :
  public CComObjectRootEx<CComSingleThreadModel>,
  public CComCoClass<CPrimeNumbers, &CLSID_PrimeNumbers>,
  public IPrimeNumbersCollImpl
{
public:
...
// IPrimeNumbers
public:
  STDMETHODIMP CalcPrimes(long min, long max) {
  m_coll.clear();
  for (long n = min; n <= max; ++n) {
    if (IsPrime(n)) {
      VARIANT var = {VT_I4};
      var.lVal = n;
      m_coll.push_back(var);
    }
  }

  return S_OK;
 }
};

If we wanted to precalculate the prime numbers but keep them as a vector of long numbers, this is how we'd use ICollectionOnSTLImpl:

// Needed for implementation of get__NewEnum.
// Converts the storage type (long) to the
// enumeration type (VARIANT).
struct _CopyVariantFromLong {
    static HRESULT copy(VARIANT* p1, long* p2) {
    if (p1->vt == VT_I4) {
      *p2 = p1->lVal;
      return S_OK;
    }
    else {
      VARIANT var;
      HRESULT hr = VariantChangeType(&var, p1, 0, VT_I4);
      if( SUCCEEDED(hr) ) *p2 = var.lVal;
      return hr;
    }
  }

  static void init(VARAINT* p) { ::VariantInit(p); }
  static void destroy(VARIANT* p) { ::VariantClear(p); }
};

// Needed for implementation of IDispatch methods
typedef IDispatchImpl<IPrimeNumbers, &IID_IPrimeNumbers>
  IPrimeNumbersDualImpl;

// Needed for implementation of get__NewEnum method
typedef CComEnumOnSTL<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT,
  _CopyLongFromVariant, vector<VARIANT> >
  CComEnumVariantOnVectorOfLongs;

// Needed for implementation of standard collection methods
typedef ICollectionOnSTLImpl<IPrimeNumbersDualImpl,
  vector<long>, long, _Copy<long>,
  CComEnumVariantOnVectorOfLongs>
  IPrimeNumbersCollImpl;

class ATL_NO_VTABLE CPrimeNumbers :
  public CComObjectRootEx<CComSingleThreadModel>,
  public CComCoClass<CPrimeNumbers, &CLSID_PrimeNumbers>,
  public IPrimeNumbersCollImpl {
public:
...
// IPrimeNumbers
public:
  STDMETHODIMP CalcPrimes(long min, long max) {
    m_coll.clear();
    for (long n = min; n <= max; ++n) {
      if (IsPrime(n)) {
        m_coll.push_back(n);
      }
    }

    return S_OK;
  }
};

Finally, if we wanted to have the prime numbers calculated on demand and exposed as long numbers, we'd use ICollectionOnSTLImpl:

// Calculates prime numbers on demand
class PrimesContainer;

// Needed for implementation of get_Item.
// Converts the storage type (VARIANT) to the item type (long).
struct _CopyVariantFromLong;

// Needed for implementation of IDispatch methods
typedef IDispatchImpl<IPrimeNumbers, &IID_IPrimeNumbers>
  IPrimeNumbersDualImpl;

// Needed for implementation of get__NewEnum method
typedef CComEnumOnSTL<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT,
  _CopyVariantFromLong, PrimesContainer >
  CComEnumVariantOnPrimesContainer;

// Needed for implementation of standard collection methods
typedef ICollectionOnSTLImpl<IPrimeNumbersDualImpl,
  PrimesContainer, long, _Copy<long>,
  CComEnumVariantOnPrimesContainer>
  IPrimeNumbersCollImpl;

class ATL_NO_VTABLE CPrimeNumbers :
  public CComObjectRootEx<CComSingleThreadModel>,
  public CComCoClass<CPrimeNumbers, &CLSID_PrimeNumbers>,
  public IPrimeNumbersCollImpl {
public:
...
// IPrimeNumbers
public:
  STDMETHODIMP CalcPrimes(long min, long max)
  { m_coll.SetRange(min, max); }
};

Jim Springfield, the father of ATL, says "ICollectionOnSTLImpl is not for the faint of heart." He's absolutely right. It provides a lot of flexibility, but at the expense of complexity. Still, when you've mastered the complexity, as with any good class library, you can get a lot done with very little code.


previous page
next page
Converted from CHM to HTML with chm2web Pro 2.75 (unicode)