Iterators
Iterators generalize the iteration over elements of an arbitrary collection. This allows iteration over arrays, lists, maps, trees, etc. in a unified way.
Creating an iterator is as simple as creating a CxIterator
struct and setting the fields in a meaningful way. The UCX collections provide various functions to create such iterators.
If the predefined fields are insufficient (or introduce too much bloat) for your use case, you can alternatively create your own iterator structure and place the CX_ITERATOR_BASE
macro as first member of that structure.
Creating an Iterator
The following functions create iterators over plain C arrays.
The cxIterator()
function creates an iterator over the elements of array
where each element is elem_size
bytes large and the array contains a total of elem_count
elements. The cxMutIterator()
function creates an equivalent mutating iterator.
The cxIteratorPtr()
and cxMutIteratorPtr()
functions are equivalent to the cxIteratorPtr()
and cxMutIteratorPtr()
, except they assume sizeof(void*)
as the elem_size
.
The UCX collections also define functions for creating iterators over their items. You can read more about them in the respective Sections of the documentation.
Using an Iterator
The following macros work with arbitrary structures using CX_ITERATOR_BASE
and invoke the respective function pointers valid
, current
, or next
.
You may use them for manual iterator, but usually you do not need them. Every iterator can be used with the cx_foreach
macro.
The macro takes three arguments:
the pointer-type of a pointer to an element,
the name of the variable you want to use for accessing the element,
and the iterator.
Mutating Iterators
Usually an iterator is not mutating the collection it is iterating over. But sometimes it is desirable to remove an element from the collection while iterating over it.
For this purpose, most collections allow the creation of a mutating iterator. On mutating iterators the mutating
flag in the base structure is set to true
, and it is allowed to call the cxFlagForRemoval()
function, which instructs the iterator to remove the current element from the collection on the next call to cxIteratorNext()
and clear the flag afterward. If you are implementing your own iterator, it is up to you to implement this behavior.
Passing Iterators to Functions
To eliminate the need of memory management for iterators, the structures are usually used by value. This does not come with additional costs, because iteration is implemented entirely by macros.
However, sometimes it is necessary to pass an iterator to another function. To make that possible in a generalized way, such functions should accept a CxIteratorBase*
pointer which can be obtained with the cxIteratorRef()
macro on the calling site.
In the following example, elements from a list are inserted into a tree:
Custom Iterators
The base structure is defined as follows:
The valid
function indicates whether the iterator is currently pointing to an element in the collection. The current
function is supposed to return that element, and the next
function shall advance the iterator to the next element. The booleans mutating
and remove
are used for mutating iterators as explained above.
Iterators may be wrapped in which case the original implementation can be stored in current_impl
and called by a wrapper implementation pointed to by current
. This can be useful when you want to support the store_pointer
field of the Collections API.
A specialized, simple, and fast iterator over an array of a certain type, that does not support mutation, can be implemented as follows: