47
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;
}

Is there a way with template trick (or other) to get the same syntax in c++?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Guillaume Paris
  • 10,303
  • 14
  • 70
  • 145
  • Raymond Chen broke down what `yield` does behind the scenes in http://blogs.msdn.com/b/oldnewthing/archive/2008/08/12/8849519.aspx. – Bill Aug 27 '11 at 14:55
  • @Bill The link above is unreachable – Michael IV Aug 10 '22 at 18:37
  • @MichaelIV Microsoft migrates their blogs every 5 years or so which breaks old links. Here you go: https://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273 – Bill Sep 09 '22 at 12:48

11 Answers11

31

Take a look at boost::Coroutine. It does what you want. http://www.crystalclearsoftware.com/soc/coroutine/index.html#coroutine.intro

Example from tutorial

http://www.crystalclearsoftware.com/soc/coroutine/coroutine/tutorial.html

int range_generator(generator_type::self& self, int min, int max) 
{
     while(min < max)
         self.yield(min++);
     self.exit();
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Łukasz Milewski
  • 1,897
  • 13
  • 14
  • 2
    +1, that's really interesting and I have little/no idea how `self.exit()` is a legitimate replacement for a return statement. (I have my suspicions it's some horrific abuse of exceptions or `longjmp`, but I'm not sure I want to know!) – Flexo Aug 27 '11 at 12:33
  • 4
    Boost.Coroutine is implemented in assembly, and through OS calls on platforms that support "Fibers". It is not implemented in pure C++. – Mankarse Aug 27 '11 at 15:06
  • I do not see coroutines in the list of boost libraries on the official site. Any pointers? – Noah Watkins Jan 30 '12 at 03:29
  • 12
    If this is for Win32, please, please, please understand that using Fibers in *any* code is an extremely advanced topic, and seeing libraries which effectively hide away the Fiber code is really scary. There's a whole boatload of Win32 APIs which don't work in the presence of Fibers, or more scarily don't work as expected. For example locks in Win32 are based off the thread id - this means for Fibers, if you take a lock then yield, another Fiber running on your thread can also successfully take the lock aswell! So unless you're really careful it can bite you hard. – Mike Vine Aug 29 '13 at 10:57
15

You can always code this by hand. Truthfully, yield really seems like sugar coating to me (and co-routines too).

What a coroutine is, really ? Some state bundled up together with:

  • one function to create it (isn't it a constructor ?)
  • one function to move to the next state (isn't it operator++, traditionally ?)

In C++, it's called an InputIterator, and can be arbitrarily fat.

So, it's true that the syntax won't be as pretty, but this should do, just with the Standard Library:

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

And obviously, since you decide exactly what state is stored, you decide if all is pre-computed or if part (or whole of it) is lazily computed, and possibly cached, and possibly multi-threaded, and ... you got the idea :)

Sid Vishnoi
  • 1,250
  • 1
  • 16
  • 27
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 42
    I don't see why "sugar coating" is such a bad thing. If you go down to it, a class is also nothing more than sugar coating, the same goes for loops and so on. And the obvious problem with the hands on approach is, that you basically have to write an arbitrarily complex state machine (and I can think of several real world applications where this wouldn't be that easy) – Voo Aug 27 '11 at 13:49
  • 4
    @Voo: sugar coating introduces complexity, simply put -> there is more to learn. The OP asked about `yield` in C++, my take is that instead of "porting" C# syntax into C++, it's best to reflect on what it's doing and find what's idiomatic in C++. A co-routine is nothing more that an `InputIterator`. – Matthieu M. Aug 27 '11 at 14:39
  • 24
    I don't agree with "introduces complexity" - generator semantics are simple and easy to understand in my experience (and if there's one language that doesn't follow the "simplest syntax possible" approach it's c++!). Also it's not C# syntax, but a well known concept in CS, implemented in lots of languages (and certainly not the same as an InputIterator!). Implementing a state machine manually for some function is in many cases highly non-trivial. Eg try implementing [this](http://snippets.dzone.com/posts/show/753) with an InputerIterator - certainly harder to understand – Voo Aug 27 '11 at 15:17
  • @Voo: Then we are of different opinion, I prefer a lean core language :) (and readily agree that C++ could be trimmed down) Your example is a nice approach in Python, in C++, I'd trust the Standard Library though, and use `std::next_permutation`. That is what idiomatic is about in my opinion. – Matthieu M. Aug 27 '11 at 16:32
  • 3
    Matthieu, what's a `for` loop but sugar coating over a `while` loop? What's a `switch` but a cascade of `if`? Syntactic sugar isn't necessarily a bad thing, for without it we'd still punch hex op codes directly into memory. It's only a question of where you draw the line. You seem to draw it somewhere between a language with only one loop statement plus one branch statement and a language including `yield`. Others include `yield`. Me, I have used it, and see the point of it, but could live either with or without it. – sbi Aug 27 '11 at 19:22
  • @sbi: I have used it too, in Python. The issue with `yield` is that it transforms a function into a stateful object; there is also an issue with the possibility to pass an argument to the function object to be reinjected at the point where `yield` suspended the execution (and what if there are several yields expecting differnet arguments ?), etc... All in all, I think the keyword hides too much of what's going on under the hood, and it makes it more difficult to understand what's going on. Too much sugar coating, imho. – Matthieu M. Aug 28 '11 at 11:20
  • @Matthieu: Lambda also does such a transformation and hides what's under the hood. Are you opposed to that, too? `:)` – sbi Aug 28 '11 at 11:36
  • @sbi: not as much, a lambda is a simple functor with easy semantics to capture state for the surrounding environment, not a full blown state machine (I am thinking multiple yield in one function, with the ability of injecting values at each suspension). – Matthieu M. Aug 28 '11 at 12:09
  • 1
    @Matthieu M. Yes and looking at the implementation of `next_permutation` pretty much proves my point about it being several times more complex (after all that was only an example not the only use case). And I've never heard of reinjecting arguments into a suspended function - and not one of the languages listed on Wikipedia seem to have that functionality. And isn't the whole point of "sugar coating" hiding stuff that can be done by the compiler but would be quite complex for the programmer? Seems to me c++, contrary to c, abstracts quite a lot away. – Voo Aug 28 '11 at 14:45
  • @Voo: Python supports it (it didn't in the original implementation, but it's been improved since). You can read more about it in the PEP 342 (http://www.python.org/dev/peps/pep-0342/), unfortunately this does not work so well with C++ static typing (as soon as you'd need multiple yield expecting different types). – Matthieu M. Aug 28 '11 at 16:20
  • @Matthieu M. Interesting, didn't know that - always nice to learn something new (thanks!). I assume this could be useful in some cases but I agree that this adds complexity to the rather simple concept (you can't have arguments for the first call and so on). To me this seems more like a large step towards coroutines. – Voo Aug 28 '11 at 18:45
  • 1
    @Matthieu: So the transformations done for lambda functions are Ok, the ones needed for `yield` wouldn't. Isn't this just what I said? It all boils down to where you draw the line. `:)` – sbi Aug 29 '11 at 06:49
  • They're sugar in C# too. But also, isn't all programming "just" sugar syntax for binary? It takes what is normally lines of complicated code into a single keyword. This isn't a bad thing – AustinWBryan Feb 11 '23 at 05:32
14

Coroutines are in the standard library since C++20 and uses co_yield instead of yield.

See also: What are coroutines in C++20?

There are some example usages in the first link: (the second one is probably what you're looking for)

  • uses the co_await operator to suspend execution until resumed

    task<> tcp_echo_server() {
       char data[1024];
       while (true) {
          size_t n = co_await socket.async_read_some(buffer(data));
          co_await async_write(socket, buffer(data, n));
       }
    }
    
  • uses the keyword co_yield to suspend execution returning a value

    generator<int> iota(int n = 0) {
       while (true)
          co_yield n++;
    }
    
  • uses the keyword co_return to complete execution returning a value

    lazy<int> f() {
       co_return 7;
    }
    
Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
13

In C++14, you can mimic yield this way:

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

A live example is available at http://ideone.com/SQZ1qZ

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Feng Wang
  • 1,506
  • 15
  • 17
  • 2
    I didn't but the live example can easily fit in your answer without displaying ads from ideone.com. – rwst Apr 20 '18 at 06:47
  • 14
    Isn't the purpose of `yield` to prevent the series of objects (`int[]` in this case) from being immediately put into memory? – Marc Dirven Jan 20 '20 at 11:09
  • The example is still valid, precalculating an array is not required, take the next example. `#include int main() { auto&& function = [](int i0) { int i = i0; return [=]() mutable { i *= 2; return i;}; }; auto fn = function(5); for ( unsigned long i = 0; i != 10; ++i ) std::cout << "\t" << fn() << "\t|"; std::cout << "\n"; return 0; }` – karel Apr 25 '23 at 12:45
4

Here is ASM "roll your own" version : 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's 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();
}

*/  
Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
Michael IV
  • 11,016
  • 12
  • 92
  • 223
  • Very nice, but is this cross-platform? – xilpex May 01 '20 at 20:51
  • Nobody should use this, if you do, remove it. This involves #UB as your stack data can be overwritten by anything you do until you resume. – Alexis Paques Aug 11 '22 at 13:34
  • @AlexisPaques That's not undefined behaviour, that's just having to manually deal with the stack, which is pretty much what we'd expect if we use `__asm`. – c z Sep 26 '22 at 12:38
  • All local variables are broken here. A proper implementation needs to save the context to be able to resume it. It is not the case here. – Alexis Paques Sep 29 '22 at 11:22
  • https://thisisub.godbolt.org/z/fh87sd1c4 - Feel free to check by yourself :) – Alexis Paques Sep 29 '22 at 11:38
2

If all what you need is just foreach-like stuff, then following syntax is available in C++:

#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

If you need a little bit of coroutine "power", then you can try stackless coroutines.

Or if you need full power - then go with stackful coroutines. There is Boost.Coroutine library which implements stackful coroutines for different platforms.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
Evgeny Panasyuk
  • 9,076
  • 1
  • 33
  • 54
1

If you write static unsigned int checkpoint = 0;, make all your variables static, switch (checkpoint), set each case: goto to some label, above each return set checkpoint to unique value, and below define label, and at the end of the function set checkpoint to zero, and all static variables to their default value, and at last return the end value of the function. If you do all this then the function becomes enumerable and iterative. The two lines you add above and below each return line, makes the return command to behave like yield return. goto allows you to continue and resume where you left off, and static integer variable, like checkpoint, help you to remember where you stopped, from where to continue/resume and where to go. You test it's values with switch case statements. Making all other variables static, is to save their value to the next call, so in the next call, their value won't be reset!

Here for example:

#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;
    }
}

The above program produces the following output:

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

1

Something similar is proposed for C++17 and there is already an experimental implementation in Visual C++ 2015. Here's a good overview talk from Gor Nishanov, one of the main authors of the proposal.

mattnewport
  • 13,728
  • 2
  • 35
  • 39
1

An try to implement yield in c++ coroutine

Guillaume Paris
  • 10,303
  • 14
  • 70
  • 145
0
#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;
    }
};

To use the code...

int iret; 
subclass *sc;

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

Just got this working; it seems quite simple now, although I had a few false starts with it :)

log0
  • 10,489
  • 4
  • 28
  • 62
Ian
  • 111
  • 1
  • 2
-2

You can of course always write your own iterators and return from them whatever you desire, but why would you want to? In the given example, why not simply put your values into a container like vector and iterate over that?

TeaWolf
  • 714
  • 5
  • 10
  • 4
    Consider a situation were the values are to be computed. You might want to have lazy evaluation of the sequence of values, which is what the code shown does. You could write a function that returns an infinite list that way. – R. Martinho Fernandes Aug 27 '11 at 10:20
  • @TeaWolf In a real application, there would be more than a few integers and they may not be integers at all, but something much more expensive. Maybe the caller just wants to find the element that fulfills certain condition - storing elements in a container would not just waste space, but also the time because elements that are after the desired element would be calculated unnecessarily. Also, `std::vector` needs to go through reallocate/copy routine to increase its size (unless known in advance, which in general case you don't need to know for iterator blocks). – Branko Dimitrijevic Aug 27 '11 at 10:34
  • @TeaWolf One nice example where a generator makes for an extremely nice and simple function is eg [this](http://snippets.dzone.com/posts/show/753) - it's python, but you should get the gist of it anyhow. – Voo Aug 27 '11 at 13:51
  • In Python I am used to yield a lot and miss it in C++. The best example is where I want to hide the MYSQL implementation. For example, the top level wants to know tables in a restaurant, but not see the SQL implementation:: for (x : sql.getAllTables()) ... and the function sql.getAllTables() { sql.query("select id, name from some_table order by name", for (x in result) yield one_table } .. – Bart Mensfort Nov 21 '17 at 22:15