├── README.md └── generator.hpp /README.md: -------------------------------------------------------------------------------- 1 | # cpp-generators 2 | Generators for C++ 3 | 4 | ## Introduction 5 | 6 | Iterators in C++ is a good but not a perfect abstraction. The concept of `foreach()` (D, Python, Ruby, etc.) appears as a more generic solution. At least, `foreach()` does not require an artificial `iterator::end()` to be defined for the collection. 7 | 8 | The `foreach()` abstraction can be imagined as some function/object that returns the next value of collection/sequence each time it gets invoked. Such functions are known as generators. 9 | 10 | Generators are functional objects that persist internal state between invocations thus they can be considered as corroutines too. 11 | 12 | ## Background 13 | 14 | This version of $generator's for C++ is based on the bright [idea of Simon Tatham - "coroutines in C"](http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html). In particular, on the idea of using `switch`/`case` for the implementation. 15 | 16 | ## Declaring a Generator 17 | 18 | To declare a generator, you will use `$generator`, `$yield`, `$emit`, and `$stop` "keywords" that are macro definitions in fact. 19 | 20 | Here is a typical implementation of a generator. In this particular example our generator will emit numbers from 10 to 1 in descending order: 21 | 22 | ```cpp 23 | include "generator.hpp" 24 | 25 | $generator(descent) 26 | { 27 | // place for all variables used in the generator 28 | int i; // our counter 29 | 30 | // place the constructor of our generator, e.g. 31 | // descent(int minv, int maxv) {...} 32 | 33 | // from $emit to $stop is a body of our generator: 34 | 35 | $emit(int) // will emit int values. Start of body of the generator. 36 | 37 | for (i = 10; i > 0; --i) 38 | $yield(i); // a.k.a. yield in Python, 39 | // returns next number in [1..10], reversed. 40 | $stop; // stop, end of sequence. End of body of the generator. 41 | }; 42 | ``` 43 | 44 | Having such a descending generator declared, we will use it as: 45 | 46 | ```cpp 47 | int main(int argc, char* argv[]) 48 | { 49 | descent gen; 50 | for(int n; gen(n);) // "get next" generator invocation 51 | printf("next number is %d\n", n); 52 | return 0; 53 | } 54 | ``` 55 | 56 | The `gen(n)` there is in fact an invocation of the `bool operator()(int& v)` method defined "under the hood" of our generator object. It returns `true` if the parameter `v` was set, and `false` if our generator cannot provide more elements - was stopped. 57 | 58 | ## Limitations of the Approach 59 | 60 | `$yield` cannot be placed inside a `switch` statement as `$emit()` declares a `switch` by itself. 61 | 62 | ## License 63 | 64 | The generator.hpp is licensed under [The BSD License](https://opensource.org/licenses/bsd-license.php). 65 | 66 | ## Async/await usage 67 | 68 | Please check [async.hpp](https://github.com/c-smile/async.hpp) if you need to use this approach in asynchronous code. 69 | -------------------------------------------------------------------------------- /generator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __generator_hpp__ 2 | #define __generator_hpp__ 3 | 4 | //| 5 | //| micro generator/coroutine/continuation for C++ 6 | //| author: Andrew @ sciter.com 7 | //| idea borrowed from: "coroutines in C" by Simon Tatham, 8 | //| http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html 9 | //| 10 | //| License: BSD 11 | //| 12 | namespace generator 13 | { 14 | //++ coroutine, generator, continuation for C++ 15 | 16 | struct instance 17 | { 18 | protected: 19 | int _line; 20 | public: 21 | instance() { rewind(); } 22 | void rewind() { _line = 0; } // restart the generator 23 | 24 | private: 25 | instance( const instance& ); // non-copyable 26 | instance& operator=( const instance& ); // non-assignable 27 | }; 28 | 29 | // Declaration 30 | #define $generator(NAME) struct NAME : public generator::instance 31 | 32 | // Start of generator's body, value[s] emmiter. 33 | // Shall end with $stop and contain at least one $emit[n] 34 | #define $emit(T) bool operator()(T& _rv) { switch(_line) { case 0:; 35 | #define $emit2(T1,T2) bool operator()(T1& _rv1, T2& _rv2) { switch(_line) { case 0:; 36 | #define $emit3(T1,T2,T3) bool operator()(T1& _rv1, T2& _rv2, T3& _rv3 ) { switch(_line) { case 0:; 37 | #define $emit4(T1,T2,T3,T4) bool operator()(T1& _rv1, T2& _rv2, T3& _rv3, T4& _rv4 ) { switch(_line) { case 0:; 38 | 39 | // yields next value(s) 40 | #define $yield(V) { _line=__LINE__; _rv = (V); return true; case __LINE__: _line=__LINE__; } 41 | #define $yield2(V1,V2) { _line=__LINE__; _rv1 = (V1); _rv2 = (V2); return true; case __LINE__: _line=__LINE__; } 42 | #define $yield3(V1,V2,V3) { _line=__LINE__; _rv1 = (V1); _rv2 = (V2); _rv3 = (V3); return true; case __LINE__: _line=__LINE__; } 43 | #define $yield4(V1,V2,V3,V4) { _line=__LINE__; _rv1 = (V1); _rv2 = (V2); _rv3 = (V3); _rv4 = (V4); return true; case __LINE__: _line=__LINE__; } 44 | 45 | // end of value[s] emmiter 46 | #define $stop } _line = 0; return false; } 47 | 48 | //-- coroutine, generator, continuation for C++ 49 | 50 | } 51 | 52 | #endif 53 | --------------------------------------------------------------------------------