Enumerators

A frequent programming task is to generate some sequence of items and iterate through that sequence. A number of places within COM and OLE require this sort of functionality, such as where a client wants to obtain a list of items from an object, regardless of what type of boundary exists between the two. For example, a client that is a data consumer usually wants a list of formats that a data source (that implements IDataObject) can render for it. When asked for such a list, the source returns an interface pointer to a separate object called an enumerator. Enumerators cleanly encapsulate, behind an interface, the knowledge of how to iterate through a sequence of items from the client's desire to iterate, thereby allowing the enumerator to be implemented in whatever way is most convenient—array, linked list, and so forth.10

Enumerators are so named because they implement a type-specific interface that always has the prefix IEnum followed by the name of a type. For example, OLE defines IEnumUnknown (to enumerate a list of objects by their IUnknown pointers) and IEnumFORMATETC (to enumerate FORMATETC structures from data objects). There is also the interface IEnumVARIANT, which enumerates data structures named VARIANT—a big union of different data types, as we'll see later with OLE Automation. A generic enumerator with IEnumVARIANT is usually called a collection, which is an appropriate term for any enumerator, literally a collection of things.

Regardless of the type involved, all IEnum* interfaces contain the same member functions, although the specific argument types vary with the enumerated type. The difference among enumerator interfaces is strictly in the types involved; the semantics are always the same. Enumerators in general can be described as a parameterized type, using a C++ template syntax (although actual templates are not used to define enumerator interfaces in header files).


template <class ELT_T> interface IEnum : IUnknown
{
virtual HRESULT Next(ULONG celt, ELT_T *rgelt
, ULONG *pceltFetched)=0;
virtual HRESULT Skip(ULONG celt)=0;
virtual HRESULT Reset(void)=0;
virtual HRESULT Clone(IEnum<ELT_T> ** ppEnum)=0;
};

In this template syntax, ELT_T stands for "ELemenT Type" and is a placeholder for whatever specific type is applicable to the enumerator, such as IUnknown, FORMATETC, VARIANT, and so forth. By itself, elt means "element."

The following table describes the member functions of an enumerator interface, omitting the omnipresent IUnknown members:

Function

Description

Next

Returns the next celt elements of the list, starting at the current index. When the enumerated type is itself an interface pointer, as with IEnumUnknown, the Next function must call AddRef through each pointer before return, and the client must later call Release through each pointer.

Skip

Skips past celt elements in the list.

Reset

Sets the current index to 0.

Clone

Returns a new enumerator object with the same items but an independent index. The items in the clone are not guaranteed to be in the same order as the original enumerator.


Note that enumerators are separate and independent objects, although in almost every circumstance a client obtains an enumerator by asking another object for the pointer. This is the second method that we saw earlier through which a client obtains its first interface pointer to a new object. If the object providing the enumerator doesn't have anything to enumerate, it will return S_FALSE and a NULL interface pointer from the appropriate member function.

10 Note that OLE Automation has a data type called a Safe Array that can be used to pass arrays along with information about the array dimensions and bounds when an enumerator is not necessary.