(For a general introduction to and discussion of iterators, see here.)

Though Big Denominator hopes that our expression template facilities limit the need for and usage of iterators for array/vector containers, iterators are often necessary - particularly for maps and sets. Big Denominator containers come with two types of iterators:

STL-like Iterators

Big Denominator containers have STL-like iterators; in the case of maps and sets, these iterators are exactly the associated STL iterators. Hence, containers have begin() and end() methods. This allows the containers to be used in STL algorithms.

For convenience, Big Denominator provides a FOREACH macro for sequential iteration through containers.

FOREACH(ctnr, itr)

is shorthand for

for (auto itr(ctnr.begin()); itr != ctnr.end(); itr++)

where ctnr is the container over which to iterate and itr is the name of the iterator. For example, if myArray is an array of integers, then the following code will print all elements in the array:

FOREACH(myArray, itr)
{
    std::cout << *itr << endl;
}`

Smart Iterators

Big Denominator's Smart Iterators utilize smart (shared) pointers to the underlying container to

  1. simplify iteration; and
  2. guarantee the container's existence in scope.

A Smart Iterator requires a smart pointer to the container over which it iterates; this differs fundamentally from the STL-like iterators which are generated by their containers (e.g., cntr.begin()). Both const and non-const versions exist.

Smart Iterators are typically constructed using the GetItr and GetConstItr utilities in the iterators namespace. For example, if myMapPtr is a smart pointer to a map, then the following two statements produce const and non-const Smart Iterators, respectively:

auto citr(iterators::GetConstItr(myMapPtr));

auto itr(iterators::GetItr(myMapPtr));

Because the Smart Iterator holds a smart pointer to the underlying container, the container is guaranteed to outlive the iterator. Additionally, the iterator itself can check to see if it is at the beginning or end as well as be told to move to either the beginning or end.

for (itr.begin(); !itr.isEnd(); itr++)

For convenience, Big Denominator provides a LOOP macro which simplifies the above statement to the following:

LOOP(itr)

For example, if myArrayPtr is a smart pointer to an array of integers, then the following code will print all elements in the array:

auto itr(iterators::GetItr(myArrayPtr));
LOOP(itr)
{
    std::cout << *itr << endl;
}

Arrays / Vectors

Smart Iterators for arrays and vectors have the following two methods for convenience:

  • key() returns the index of the current position. There is no STL-like equivalent.
  • value() yields a (const) reference to the value at the current position. This is equivalent to dereferencing (*) a vector's STL-like iterator.

Maps / Sets

Smart Iterators for maps and sets have the following two methods for convenience:

  • key() returns the key of the current key-value pair. This is equivalent to "->first" with the map/set's STL-like iterator.
  • value() yields a (const) reference to the value of the current key-value pair. This is equivalent to "->second" with the map/set's STL-like iterator.

Iterator Bundles

Smart Iterators allow us to build iterator-tuples for synchronized iteration through multiple, heterogeneous containers. These are referred to as Iterator Bundles. Because iterator bundles contain heterogeneous iterators, they have limited or reduced functionality - simple forward iteration.

Iterator bundles can be constructed in two ways.

  1. Bundle existing Smart Iterators; or
  2. MakeBundle / MakeBundleConst from existing containers.

For example, let

  • myArrayPtr be a smart pointer to an array;
  • itrArray be a Smart Iterator to myArrayPtr;
  • myMapPtr be a smart pointer to a map;
  • itrMap be a Smart Iterator to myMapPtr.

The following two statements produce identical bundles:

auto itr1(iterators::Bundle(itrArray, itrMap));

auto itr2(iterators::MakeBundle(myArrayPtr, myMapPtr));

Accessing the individual iterators in a bundle is done through the At method. Note that order matters - the first iterator in Bundle (or the iterator to the first container in MakeBundle) is At position 0:

itr1.At<0>()

The iterator bundle can be used like a Smart Iterator:

auto itr(iterators::Bundle(itrArray, itrMap));
LOOP(itr)
{
    std::cout << *itr.At<0>() << endl;
    std::cout << itr.At<1>().key() << endl;
    std::cout << itr.At<1>().value() << endl;
}