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.
|