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!
I do not think that it may boost development. Take into account, plase, that this approach forces a developer to know how it’s implemented. Needless to say that it uses MACROs!!! The idea is great, somehow
why “do” end “while (0)
in yield ?
Yes, in principle it could be just scope block:
Simon initially used do/while(0) so I just left it “as is”.
But some form of ‘{‘ ‘}’ brackets is definitely needed there to
handle properly things like if(condition) $yield(something);
Here is what I’ve ended up with:
It also has $yield2 when you need to emit two values.