13    Enumerators and Enumerator Interfaces

A frequent programming task is that of iterating through a sequence of items. The COM interfaces are no exception: there are places in several interfaces described in this specification where a client of some object needs to iterate through a sequence of items controlled by the object. COM supports such enumeration through the use of ``enumerator objects.'' Enumerators cleanly separate the caller's desire to loop over a set of objects from the callee's knowledge of how to accomplish that function.

[Footnote 61] Enumerators are just a concept; there is no actual interface called IEnumerator or IEnum or the like. This is due to the fact that the function signatures in an enumerator interface must include the type of the things that the enumerator enumerates. As a consequence, separate interfaces exist for each kind of thing that can be enumerated. However, the difference in the type being enumerated is the only difference between each of these interfaces; they are all used in fundamentally the same way. In other words, they are ``generic'' over the element type. This document describes the semantics of enumerators using a generic interface IEnum() and the C++ parameterized type syntax where ELT_T, which stands for ``ELemenT Type'', [Footnote 61] is representative of the type involved in the enumeration:

[
    object,
    uuid(<IID_IEnum <ELT_T>>),  // IID_IEnum<ELT_T>
    pointer_default(unique)
]
interface IEnum<ELT_T> : IUnknown
{
    HRESULT Next( [in] ULONG celt, [out] IUnknown **rgelt,
      [out] ULONG *pceltFetched );
    HRESULT Skip( [in] ULONG celt );
    HRESULT Reset( void );
    HRESULT Clone( [out] IEnum<ELT_T>**ppenum );
}

A typical use of an enumerator is the following.

//Somewhere there's a type called ``String''
typedef char * String;
//Interface defined using template syntax
typedef IEnum<char *> IEnumString;
...
interface IStringManager { 
 virtual IEnumString* EnumStrings(void) = 0;
 };
...
void SomeFunc(IStringManager * pStringMan)  {
 char *        psz;
 IEnumString * penum;
 penum=pStringMan->EnumStrings();
 while (S_OK==penum->Next(1, &psz, NULL))
  {
  //Do something with the string in psz and free it
  }
 penum->Release();
 return;
 }

 

IEnum::Next()

NAME

IEnum::Next() - Attempt to get the next celt items in the enumeration sequence.

Synopsis

HRESULT IEnum::Next(
        ULONG celt,
        ELT_T* rgelt,
        ULONG* pceltFetched );

Description

Attempt to get the next celt items in the enumeration sequence, and return them through the array pointed to by rgelt. If fewer than the requested number of elements remain in the sequence, then just return the remaining ones; the actual number of elements returned is passed through *pceltFetched (unless it is NULL). If the requested celt elements are in fact returned, then return S_OK; otherwise return S_FALSE. An error condition other than simply ``not that many elements left'' will return an SCODE which is a failure code rather than one of these two success values.

Parameters

[Footnote 62]

celt

The number of elements that are to be returned.

rgelt2

An array of size at least celt in which the next elements are to be returned. [Footnote 62]

pceltFetched

May be NULL if celt is one. If non-NULL, then this is set with the number of elements actually returned in rgelt.

Return Values

S_OK

Success. On exit all the celt elements requested are valid and returned in rgelt.

S_FALSE

Success. On exit only the first *pceltFetched entries of rgelt are valid. The contents of the remaining entries in the rgelt array are indeterminate.

E_UNEXPECTED

An unknown error occurred. On exit no entries in the rgelt array are valid; they are all in an indeterminate state.

 

IEnum::Skip()

NAME

IEnum::Skip() - Attempt to skip over the next celt elements in the enumeration sequence.

Synopsis

HRESULT IEnum::Skip(
        ULONG celt );

Description

Attempt to skip over the next celt elements in the enumeration sequence. Return S_OK if this was accomplished, or S_FALSE if the end of the sequence was reached first.

Parameters

celt

The number of elements that are to be skipped.

Return Values

S_OK

Success. The requested number of elements were skipped.

S_FALSE

Success. Some skipping was done, but the end of the sequence was hit before the requested number of elements could be skipped.

E_UNEXPECTED

An unknown error occurred.

 

IEnum::Reset()

NAME

IEnum::Reset() - Reset the enumeration sequence back to the beginning.

Synopsis

HRESULT IEnum::Reset(
        void );

Description

Reset the enumeration sequence back to the beginning.

Note that there is no intrinsic guarantee that exactly the same set of objects will be enumerated the second time as was enumerated the first. Though clearly very desirable, whether this is the case or not is dependent on the collection being enumerated; some collections will simply find it too expensive to maintain this condition. Consider enumerating the files in a directory, for example, while concurrent users may be making changes.

Return Values

S_OK

Success. The enumeration was reset to its beginning.

E_UNEXPECTED

An unknown error occurred.

 

IEnum::Clone()

NAME

IEnum::Clone() - Return another enumerator which contains exactly the same enumeration state as this one.

Synopsis

HRESULT IEnum::Clone(
        IEnum<ELT_T>** ppenum );

Description

Return another enumerator which contains exactly the same enumeration state as this one. Using this function, a client can remember a particular point in the enumeration sequence, then return to it at a later time. Notice that the enumerator returned is of the same actual interface as the one which is being cloned.

Caveats similar to the ones found in IEnum::Reset() regarding enumerating the same sequence twice apply here as well.

Parameters

ppenum

The place in which to return the clone enumerator.

Return Values

S_OK

Success. The enumeration was reset to its beginning.

E_UNEXPECTED

An unknown error occurred.