Подтвердить что ты не робот

Эквивалент в С++ доходности в С#?

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

Есть ли способ с трюком шаблона (или другим), чтобы получить тот же синтаксис в С++?

4b9b3361

Ответ 2

Вы всегда можете указать это вручную. По правде говоря, yield действительно кажется мне сахарным покрытием (и совместными занятиями тоже).

На самом деле, что такое coroutine? Некоторое государство собрано вместе с:

  • одна функция для его создания (разве это не конструктор?)
  • одна функция для перехода к следующему состоянию (не традиционно ли это operator++?)

В C++ он называется InputIterator и может быть произвольно толстым.

Итак, верно, что синтаксис не будет таким же красивым, но это должно быть сделано, как раз со стандартной библиотекой:

static std::array<int, 6> const Array = {{1, 2, 4, 8, 16, 16777216}};

class Integers: public std::iterator<std::input_iterator_tag,
                                      int, ptrdiff_t, int const*, int>
{
public:
  Integers(): _index(0) {}

  operator bool() const { return _index < Array.size(); }

  Integers& operator++() { assert(*this); ++_index; return *this; }
  Integers operator++(int) { Integers tmp = *this; ++*this; return tmp; }

  int operator*() const { assert(*this); return Array[_index]; }
  int const* operator->() const { assert(*this); return &Array[_index]; }

private:
  size_t _index;
}; // class Integers

И, разумеется, поскольку вы точно определяете, какое состояние хранится, вы решаете, все ли они предварительно вычислены или если часть (или вся ее) лениво вычисляется и, возможно, кэшируется и, возможно, многопоточна, и... вы получили идея :)

Ответ 3

В С++ 14 вы можете имитировать yield следующим образом:

auto&& function = []() { 
    int i = 0; 
    return [=]() mutable { 
        int arr[] = {1,2,4,8,16,16777216}; 
        if ( i < 6 ) 
            return arr[i++]; 
        return 0; 
    }; 
}();

Живой пример доступен на http://ideone.com/SQZ1qZ

Ответ 4

Вот версия ASM "Roll your own": http://www.flipcode.com/archives/Yield_in_C.shtml

#include <stdio.h
#include <conio.h
#include <iostream.h


//
// marks a location in the program for resume
// does not return control, exits function from inside macro
//
// yield( x, ret )
//      x : the 'name' of the yield, cannot be ambiguous in the
//          function namespace
//    ret : the return value for when yield() exits the function;

//          must match function return type (leave blank for no return type)

#define yield(x,ret)                            \
    {                                           \
        /* store the resume location */         \
        __asm {                                 \
            mov _myStaticMkr,offset label_##x   \
        }                                       \
                                                \
        /* return the supplied value */         \
        return ret;                             \
    }                                           \
    /* our offset in the function */            \
    label_##x:



//
// resumes function from the stored offset, or
// continues without notice if there not one
// stored
//
// resume()
//   <void

#define resume()                        \
    /* our stored offset */             \
    static _myStaticMkr=0;              \
                                        \
    /* test for no offset */            \
    if( _myStaticMkr )                  \
    {                                   \
        /* resume from offset */        \
        __asm                           \
        {                               \
            jmp _myStaticMkr            \
        }                               \
    }


// example demonstrating a function with an int return type
// using the yield() and resume() macros
//
// myFunc()
//   <void

int myFunc()
{
    resume();

    cout << "1\n";

    yield(1,1);

    cout << "2\n";

    yield(2,1);

    cout << "3\n";

    yield(3,1);

    cout << "4\n";

    return 0;
}



// main function
//
// main()
//   <void

void main( void )
{
    cout << "Yield in C++\n";
    cout << "Chris Pergrossi\n\n";

    myFunc();

    do

    {
        cout << "main()\n";
        cout.flush();
    } while( myFunc() );

    cout.flush();

    getch();
}


/*

// example demonstrating a function with no return type
// using the yield() and resume() macros
//
// myFunc()
//   <void

void myFunc()
{
    resume();

    cout << "1\n";

    yield(1);

    cout << "2\n";

    yield(2);

    cout << "3\n";

    yield(3);

    cout << "4\n";

    return;
}



// main function
//
// main()
//   <void

void main( void )
{
    cout << "Yield in C++\n";
    cout << "Chris Pergrossi\n\n";

    myFunc();

    for( int k = 0; k < 4; k ++ )
    {
        cout << "main()\n";
        cout.flush();

        myFunc();
    }

    cout.flush();

    getch();
}

*/  

Ответ 6

Если все, что вам нужно, это просто материал, похожий на foreach, тогда в С++ доступен следующий синтаксис:

#define GENERATOR(name) \
struct name \
{ \
    template<typename F> \
    void operator()(F yield) \
/**/
#define _ };

template<typename Gen>
struct Adaptor
{
    Gen f;
    template<typename C>
    void operator*(C cont)
    {
        f(cont);
    }
};

template<typename Gen>
Adaptor<Gen> make_adaptor(Gen gen)
{
    return {gen};
}

#define FOREACH(arg, gen) make_adaptor(gen) * [&](arg)

#include <iostream>
using namespace std;

GENERATOR(integers)
{
    yield(1);
    yield(2);
    yield(4);
    yield(8);
    yield(16777216);
}_

int main()
{
    FOREACH(int i, integers())
    {
        cout << i << endl;
    };
}

Live Demo

Если вам нужно немного "силы" сопроцессора, вы можете попробовать стекированные сопрограммы.

Или, если вам нужна полная мощность - затем перейдите со сложными сопрограммами. Существует библиотека Boost.Coroutine, которая реализует стекированные сопрограммы для разных платформ.

Ответ 7

Что-то подобное предлагается для С++ 17, и уже есть экспериментальная реализация в Visual С++ 2015. Здесь хороший обзор talk от Gor Нишанов, один из главных авторов этого предложения.

Ответ 8

Вы можете, конечно, всегда писать свои собственные итераторы и возвращаться от них, что бы вы ни пожелали, но зачем вам это нужно? В данном примере, почему бы просто не поместить ваши значения в контейнер, как вектор, и перебрать его?

Ответ 9

#include <setjmp.h>

class superclass
{
public:
    jmp_buf jbuf;
public:
    virtual int enumerate(void) { return -1; }
};

class subclass: public superclass
{
public:
    int enumerate()
    {
        static int i;
        static bool b = false;

        if(b) 
            longjmp(jbuf, 1);

        for(b = true, i = 0; i < 5; (i)++)
        {
            printf("\ndoing stuff: i = %d\n", i);

            if(setjmp(jbuf) != 1) 
                return i;    
        }
        return -1;
    }
};

Чтобы использовать код...

int iret; 
subclass *sc;

sc = new subclass();
while((iret = sc->enumerate()) != -1)
{
    printf("\nsc->enumerate() returned: %d\n", iret);
}

Просто это работало; теперь кажется довольно простым, хотя у меня было несколько фальшивых запусков:)

Ответ 10

Если вы пишете static unsigned int checkpoint = 0;, сделайте все свои переменные static, switch (checkpoint), установите каждый case: goto на какую-либо метку, над каждой return установите контрольную точку на уникальное значение, а ниже определите метку, а на конец контрольной точки набора функций до нуля и все статические переменные до их значения по умолчанию и, наконец, return конечное значение функции. Если вы все это сделаете, функция станет перечислимой и итеративной. Две строки, которые вы добавляете выше и ниже каждой строки return, заставляют команду return вести себя как yield return. goto позволяет продолжить и возобновить работу, когда вы остановились, а переменная static integer, например контрольная точка, поможет вам запомнить, где вы остановились, откуда продолжить/возобновить и куда идти. Вы проверяете значения с помощью операторов switch case. Выполнение всех остальных переменных static - сохранение их значения для следующего вызова, поэтому в следующем вызове их значение не будет reset!

Здесь, например:

#define PowerEnd INT_MIN
int Power(int number, int exponent)
{
    static unsigned int checkpoint = 0;
    static int result = 1, i = 0;
    switch (checkpoint)
    {
        case 1: goto _1;
    }
    for (i = 0; i < exponent; i++)
    {
        result *= number;
        checkpoint = 1;
        return result;
        _1:;
    }
    checkpoint = 0;
    result = 1;
    i = 0;
    return PowerEnd;
}
void main()
{
    while (true)
    {
        int result = Power(2, 8);
        if (result == PowerEnd)
            break;
        cout << result << endl;
    }
    //to print only the first 4 results (if there are at least 4 results) then
    for (int i = 0; i < 4; i++)
    {
        int result = Power(2, 8);
        if (result == PowerEnd)
            break;
        cout << result << endl;
    }
}

Вышеуказанная программа выводит следующий результат:

2   4   8   16   32   64   128   256   2   4   8   16