How can I implement a generator in C++?

2020-07-03 07:39发布

问题:

I want to know how to implement a generator , like Python, in C++? Python can use keyword "yield" to do so. But how to do it in C++?

回答1:

In C++ we have 'iterators'. One explicitly asks for an interator, explicitly increments it and dereferences it.

If you want them to be used with the standard library functions, they should mostly be derived from std::forward_iterator, and implement a number of it's functions.

An other way to mimic kindof a generator on a collection is allowing a function as argument to a member function that feeds (yields) all of it's values to that function:

struct MyCollection {
    int values[30];

    template< typename F >  
    void generate( F& yield_function ) const {
       int* end = values+30; // make this better in your own code :)
       for( auto i: values ) yield_function( *i );
    }
};

// usage:
c.generate([](int i){ std::cout << i << std::endl; });

// or pre-C++11:
struct MyFunction { 
    void operator() (int i)const { printf( "%d\n", i); }
};
MyCollection c;
c.generate( MyFunction() );


回答2:

This... Gentlemen... is pure BLACK MAGIC:

http://www.codeproject.com/Articles/29524/Generators-in-C

I've tried it, and it even works recursively. I've been using it regularly ever since. Generators, almost as first class citizens in C++. There isn't even any performance overhead.

With my deepest respect to the author



回答3:

You can't do it, really, but you can fake it. Here's a way you can fake it in C, which you can use in C++ as well.



回答4:

To elaborate on the iterator implementation: this is an example. It can be used as a loop variable, or in std algorithms.

#include <iterator>

template< typename T, typename TDiff = T >
struct TGenerator : public std::iterator<std::forward_iterator_tag,T,TDiff> {
  T from,to;
  T value;
  TDiff step;
  bool issentinel;

  TGenerator( T from, T to, TDiff step, bool sentinel = false )
    : from(from),to(to),step(step),issentinel(sentinel), value(from)
  {}

  void operator++(){ value += step; }

  const T& operator*()const { return value; }

  bool operator!=( const TGenerator& other ) const {
    return value<to;
  }

  TGenerator sentinel()const { return TGenerator(0,0,0,true); }

};


#include <algorithm>
#include <iostream>

int main()
{
  TGenerator<int> i(0,10,3);
  std::copy( i, i.sentinel(), std::ostream_iterator<int>( std::cout, " " ) );

    return 0;
}


回答5:

Calling a coroutine multiple times and getting different answers means you keep some state. The way to keep a state is objects. The way to make them look like function call is operator overloading. See http://en.wikipedia.org/wiki/Function_object.



回答6:

you can use boost.context (sorry, not on boost distribution yet, you'll have to get it from boost vault).

A typical example code would be like this:

#include <iostream>
#include <boost/context.hpp>

using namespace std;

struct Parameters {
  int par1;
  float par2;
};

boost::context c1;
boost::context c2;

void F(void* parameters) {
  Parameters& pars = *(Parameters*)parameters;
  cout << pars.par1 << endl;
  c2.jump_to(c1);
  cout << pars.par2 << endl;
};

int main() {
  c1 = boost::context::current();
  Parameters p;
  p.par1 = 8;
  c2 = boost::context::create_context( F , c1 , p );
  c1.jump_to(c2);
  p.par2 = 1.3;
  c1.jump_to(c2);
}