Generators in C++

Introduction

As we know, 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.

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.

The proposed implementation of the generator/yield feature is provided below in full.

Background

This version of generator() for C++ is based on the bright idea of Simon Tatham - "coroutines in C". In particular, on the idea of using switch/case for this implementation.

Declaring a Generator

To declare a generator, you will use $generator, $yield, $emit, and $stop "keywords" that are macro definitions in fact.

And here is a typical implementation of a generator that emits numbers from 10 to 1 in descending order:

include "generator.h"
$generator(descent)
{
// place for all variables used in the generatorint i; // our counter// place the constructor of our generator, e.g. // descent(int minv, int maxv) {...}// from $emit to $stop is a body of our generator:
$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,// returns next number in [1..10], reversed.
$stop; // stop, end of sequence. End of body of the generator.
};

The gen(n) thing 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.

As you may see, for(int n; gen(n);) looks close enough to the construction for(var n in gen) used in JavaScript for exactly the same purpose. Expressiveness is the beauty of the approach.

What do you think of this? Of course I "abuse" the mechanism of range for-loops here and maybe in future versions or other implementations of C++ this won't work anymore. But the usage and definition of the inheritors look so natural to me.

But this is just an idea and maybe somebody finds a much better solution...

Body of generator is a code with normal flow where $yield allows you to return the value from the middle of algorithm.

That is what my Proceed member function does by a simple return at an appropriate point...

c-smile wrote:

Try to write the same using your approach.

I assume what you are trying to do is: "for all collisions in hash_map show all (and only) colliding keys" (I could not find a hash_map definition in my VS2010 that compiles your code so I don't know if collision_table corresponds to the buckets in unordered_map or if the former corresponds to buckets with size > 1 only.)

Well, because the consumer cannot see when a new collision starts the usage is somewhat limited - but same is true for my implementation...

If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
Rating helpful answers is nice, but saying thanks can be even nicer.

The type of the apportation, It was a Tip/Trick and now is under articles.

I guess there was some problem while the last update. That's why I suggested you to review and change back to Tip/Trick using the info of the link

I can try it as well if you have problems

M.D.V.

If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
Rating helpful answers is nice, but saying thanks can be even nicer.

I have just realized that it already is corrected. Forget my previous answer

M.D.V.

If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
Rating helpful answers is nice, but saying thanks can be even nicer.

A very clever addition to my C++ arsenal, I've been using this daily for a while now and I'm not even planning to switch to the upcoming boost solution for generators, as these macro based ones are incredibly efficient and clean.

With your current solution you have to write a new generator for each loop. Even if you created pre-defined generators (e.g. for iterating over an integer range) the advantage of this solution wouldn't be obvious.

Here's a brief case: generators let you factor a complex loop,
dividing the value-producing part from the value-consuming part.
Neither part has to be transformed in a non-obvious way. With
iterators alone, you do this by rewriting the value-producing part as
an Iterator class. This obfuscates the iterator implementation.

On each generator invocation it returns next property of the object. Property is key/value pair.
Object contains either flat table of key/value pairs if there are less than 8 such pairs. Otherwise
properties are organized as a hash table.