Iterators#

C++ iterators are used to traverse through elements stored in a container. C++ iterators are external iterators that work in pairs, with a beginning iterator and an ending iterator. For example, std::vector has begin/end, cbegin/cend, rbegin/rend, etc.

Enumerable Support (Internal Iterators)#

Rice makes it easy to add Enumerable support to C++ classes. The Enumerable module adds internal iterator support to a Ruby class as long as it defines an each instance method.

Rice makes this easy via the define_iterator method. define_iterator creates an each method and also mixes in the Enumerable module.

For example let’s create a simple wrapper around std::vector (for full support please see std::vector).

#include <vector>
#include <rice/rice.hpp>

using namespace Rice;

extern "C"
void Init_IntVector()
{
  using IntVector = std::vector<int>;
  define_class<IntVector>("IntVector")
    .define_constructor(Constructor<IntVector>())
    .define_method<void(IntVector::*)(const IntVector::value_type&)>("push_back", &IntVector::push_back)
    .define_iterator<IntVector::iterator(IntVector::*)()>(&IntVector::begin, &IntVector::end);
}

Notice that we have to tell Rice which overloaded version of push_back, begin and end we want to expose For more information please see Overloaded Methods.

Once the interator is defined you can write standard Ruby code such as:

intVector = IntVector.new
intVector.push_back(1)
intVector.push_back(2)
intVector.push_back(3)

result = intVector.map do |value|
  value * 2
end

Where result will be [2, 4, 6].

Let’s say you also want to expose std::vector’s reverse iterator to Ruby using the method name reach. This is done by adding a third parameter to the define_iterator call, in this case it is set to reach”:

extern "C"
void Init_IntVector()
{
  define_class<IntVector>("IntVector")
    .define_iterator<IntVector::reverse_iterator(IntVector::*)()>(&IntVector::rbegin, &IntVector::rend, "reach");
}

Example Ruby code is then:

intVector = IntVector.new
intVector.push_back(1)
intVector.push_back(2)
intVector.push_back(3)

result = intVector.reach do |value|

result = Array.new
intVector.map do |value|
  result << value * 2
end

Where result will be [6, 4, 2].

Enumerator Support (External Iterators)#

Ruby supports external iterators via the Enumerator class. The define_iterator method automatically adds support for Enumerators.

Enumerators can be created by calling an iterator method without a block, in the same way you can call Array#each or other methods without a block. For example:

intVector = IntVector.new
intVector.push_back(1)
intVector.push_back(2)
intVector.push_back(3)

# Get an enumerator
enumerator = intVector.each

# Now  use it
enumerator.map |i|
  i * 2
end