Generators in C++
May 26, 2008
As we know iterators in C++ is a good but not perfect abstraction. Concept of foreach() (D, Python, Ruby, etc.) appears as more generic solution. At least foreach() does not require artificial iterator::end() to be defined for the collection.
Abstraction foreach() can be imagined as some function/object that is returning next value of collection/sequence each time it is getting invoked. Such functions are known as generators.
Here is my version of generator() for the C++. It is based on the bright idea of Simon Tatham – “coroutines in C“.
First of all sample, here is a declaration of our generator function in C++:
#include "generator.h"
generator(descent)
{
int i;
emit(int) // will emit int values. Start of body of the generator.
for (i = 10; i > 0; i--)
yield(i); // a.k.a. yield in Python - return next number in [1..10], reversed.
stop(0); // stop, end of sequence. End of body of the generator.
};
Having declared such generator we can use it as:
int main(int argc, char* argv[])
{
descent gen;
do
printf("next number is %d\n", gen());
while (gen);
return 0;
}
That is pretty simple and should be intuitively clear, no?
Here is full source of the generator.h file:
// generator/continuation for C++
// author: Andrew Fedoniouk @ terrainformatica.com
// idea borrowed from: "coroutines in C" Simon Tatham,
// http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
#ifndef __generator_h_
#define __generator_h_
struct _generator
{
int _line;
_generator():_line(-1) {}
operator bool() const { return _line != 0; }
};
#define generator(NAME) struct NAME : public _generator
#define emit(T) T operator()() { \
if(_line < 0) { _line=0;}\
switch(_line) { case 0:;
#define stop(V) } _line = 0; return (V); }
#define yield(V) \
do {\
_line=__LINE__;\
return (V); case __LINE__:;\
} while (0)
#endif // __generator_h_
Enjoy!
