Как реализовать генератор на С++?

Я хочу знать, как реализовать генератор, например Python, на C++? Python может использовать ключевое слово «доходность» для этого. Но как это сделать на С++?


person Community    schedule 07.09.2009    source источник
comment
скоро мы сможем добавить ответ с сопрограммами C++20.   -  person xtofl    schedule 18.11.2017


Ответы (6)


В C++ у нас есть «итераторы». Один явно запрашивает интератор, явно увеличивает его и разыменовывает.

Если вы хотите, чтобы они использовались со стандартными библиотечными функциями, они в основном должны быть производными от std::forward_iterator и реализовывать ряд его функций.

Другой способ имитировать вид генератора в коллекции - разрешить функцию в качестве аргумента функции-члену, которая передает (выдает) все свои значения этой функции:

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() );
person xtofl    schedule 07.09.2009

Это... господа... чистая ЧЕРНАЯ МАГИЯ:

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

Я пробовал это, и это даже работает рекурсивно. С тех пор я использую его регулярно. Генераторы, почти как граждане первого класса в C++. Нет даже накладных расходов на производительность.

С глубоким уважением к автору

person enobayram    schedule 03.02.2012
comment
Это не эта черная магия... тем не менее, это очень не похоже на C++11. - person einpoklum; 13.07.2016

Вы не можете сделать это, на самом деле, но вы можете притворяться. Вот способ, которым вы можете подделать это в C, который вы можете использовать на С++ тоже.

person dave4420    schedule 07.09.2009
comment
+1 именно то, что я собирался сказать, хотя на самом деле нет никакой разницы между подделкой и реализацией. Я подозреваю, что в C++ вам может понадобиться состояние сопрограммы в переменных-членах функтора и вызов этого с разными экземплярами, где это уместно, вместо использования глобальных переменных и вызова именованной функции, как это делает Анакин. Вы можете сделать то же самое в C с дополнительным параметром, но вряд ли захотите этого. - person Steve Jessop; 07.09.2009

Чтобы уточнить реализацию итератора: это пример. Его можно использовать как переменную цикла или в стандартных алгоритмах.

#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;
}
person xtofl    schedule 07.09.2009

Многократный вызов сопрограммы и получение разных ответов означает, что вы сохраняете какое-то состояние. Способ сохранить состояние — это объекты. Способ сделать их похожими на вызов функции — это перегрузка оператора. См. http://en.wikipedia.org/wiki/Function_object.

person artificialidiot    schedule 07.09.2009

вы можете использовать boost.context (извините, пока нет в Boost-дистрибутиве, вам придется получить его с расширить хранилище).

Типичный пример кода будет таким:

#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);
}
person lurscher    schedule 24.11.2010