C++ doesn’t have a foreach keyword, but it does have an equivalent in “range for loops”. Today we’ll implement support for them so we can easily loop over arrays and types implementing IEnumerable and IEnumerable<T>.

Table of Contents

C++’s “range for loops” look like this:

for (String str : listOfStrings)
{
	// ... do something with 'str'
}

It’s just like C#’s foreach except you just use for and : instead of foreach and in.

Just like foreach, these loops are syntax sugar that gets expanded by the compiler into a normal for loop:

for (
	auto i = begin(listOfStrings), j = end(listOfStrings);
	i != j;
	++i)
{ 
	String& str = *i;
	// ... do something with 'str'
}

Remember that auto in C++ is like var in C#, meaning the return type of the begin and end functions can be anything as long as they can take a single parameter of whatever type listOfString is.

So let’s say listOfStrings is a C# array that we’ve wrapped with Array1<String> in C++. If we want to add support for “range for loops”, we need to make begin and end functions like so:

namespace System
{
	ArrayStringIterator begin(Array1<String>& array)
	{
		return ArrayStringIterator(array, 0);
	}
 
	ArrayStringIterator end(Array1<String>& array)
	{
		// We don't need the end iterator
		return ArrayStringIterator(nullptr, array.GetLength() - 1);
	}
}

Now we need to implement ArrayStringIterator with support for the operators the loop will use: ++, !=, and *.

struct ArrayStringIterator
{
	Array1<String>& array;
	int32_t index;
 
	ArrayStringIterator(Array1<String>& array, int32_t index)
		: array(array)
		, index(index)
	{
	}
 
	ArrayStringIterator& operator++()
	{
		index++;
		return *this;
	}
 
	bool operator!=(ArrayStringIterator& other)
	{
		return index != other.index;
	}
 
	String operator*()
	{
		return array[index];
	}
};

That’s all there is to arrays, so we’ll simply output the iterator type, begin, and end functions from the code generator for every type of array specified in the JSON.

Next up is IEnumerator<T>. This works similarly, starting with begin and end:

namespace System
{
	namespace Collections
	{
		namespace Generic
		{
			ListStringIterator begin(List<String>& list)
			{
				return ListStringIterator(list);
			}
 
			ListStringIterator end(List<String>& list)
			{
				return ListStringIterator(nullptr);
			}
		}
	}
}

So far it’s basically the same except we’re using a different type of iterator. We also didn’t pass indexes to the iterator’s constructor because not all IEnumerable<T> types can be indexed. We also passed nullptr in end to indicate that it won’t be used. This is a “forward iterator” only since IEnumerator<T> doesn’t go backwards, unlike arrays. It’s also important that these functions be placed in the same namespace as the type they’re for. Since List<T> is in System.Collections.Generic, we have to put its corresponding begin and end there too.

Now let’s see how to make ListStringIterator:

struct ListStringIterator
{
	IEnumerator<String> enumerator;
	bool hasMore;
 
	// For the 'end' iterator
	// Just set to null
	ListStringIterator(decltype(nullptr))
		: enumerator(nullptr)
		, hasMore(false)
	{
	}
 
	// For the 'begin' iterator
	// Get the IEnumerator<T> and start it iterating
	ListStringIterator(IEnumerable<T> enumerable)
		: enumerator(enumerable.GetEnumerator())
	{
		hasMore = enumerator.MoveNext();
	}
 
	ListStringIterator& operator++()
	{
		hasMore = enumerator.MoveNext();
		return *this;
	}
 
	bool operator!=(ListStringIterator& other)
	{
		return hasMore;
	}
 
	String operator*()
	{
		return enumerator.GetCurrent();
	}
};

This is basically the same as what foreach loops in C# expand to. We call GetEnumerator() and then call MoveNext repeatedly on it until it returns false. We call the Current property each time to get the current value.

Again we can add this to the code generator for any type that implements IEnumerable<T> so we have “range for loop” support on all of them that are specified in the JSON config file.

Finally, there’s the plain, non-generic IEnumerable. Since there’s only one of these, it’s just added directly to the C++ bindings layer rather than going through the code generator. It looks very similar to the above code that we used for IEnumerable<T>:

namespace System
{
	namespace Collections
	{
		EnumerableIterator begin(IEnumerable& enumerable)
		{
			return EnumerableIterator(list);
		}
 
		EnumerableIterator end(IEnumerable& enumerable)
		{
			return EnumerableIterator(nullptr);
		}
	}
}
 
struct EnumerableIterator
{
	IEnumerator enumerator;
	bool hasMore;
 
	// For the 'end' iterator
	// Just set to null
	EnumerableIterator(decltype(nullptr))
		: enumerator(nullptr)
		, hasMore(false)
	{
	}
 
	// For the 'begin' iterator
	// Get the IEnumerator and start it iterating
	EnumerableIterator(IEnumerable enumerable)
		: enumerator(enumerable.GetEnumerator())
	{
		hasMore = enumerator.MoveNext();
	}
 
	EnumerableIterator& operator++()
	{
		hasMore = enumerator.MoveNext();
		return *this;
	}
 
	bool operator!=(EnumerableIterator& other)
	{
		return hasMore;
	}
 
	Object operator*()
	{
		return enumerator.GetCurrent();
	}
};

Now that we have this available, let’s look at a few examples that use “range for loops” starting with arrays:

// Make an int[3] and fill it with [10, 20, 30]
Array1<int32_t> array(3);
array[0] = 10;
array[1] = 20;
array[2] = 30;
 
// Iterate over the elements of the array with a "range for loop"
for (int32_t val : array)
{
	// Box and log the current value
	Object msg = (Object)val;
	Debug::Log(msg);
}

Now here’s IEnumerable<T>, which looks almost the same:

List<int32_t> list;
list.Add(100);
list.Add(200);
list.Add(300);
 
for (int32_t val : list)
{
	Object msg = (Object)val;
	Debug::Log(msg);
}

Finally, let’s loop over a Transform to see all its children. We can do this because Transform implements IEnumerable.

// Create a GameObject and get its transform
GameObject go;
Transform transform = go.GetTransform();
 
// Add child A
String name = "Child A";
GameObject child(name);
Transform childTransform = child.GetTransform();
childTransform.SetParent(transform);
 
// Add child B
name = "Child B";
child = GameObject(name);
childTransform = child.GetTransform();
childTransform.SetParent(transform);
 
// Add child C
name = "Child C";
child = GameObject(name);
childTransform = child.GetTransform();
childTransform.SetParent(transform);
 
// Loop over the children of the transform with a "range for loop"
for (System::Object obj : transform)
{
	Debug::Log(obj);
}

With that, we have support for the equivalent of foreach in C++. It’s a simpler, terser, less error-prone kind of loop that many find attractive. The code is now available on the GitHub project if you’d like to check it out. Happy looping!