15

For example:

int getNext(int n) {
    while (TRUE) {
        n = n+1;
        yield n;
    }
}

int main() {
    while (TRUE) {
        int n = getNext(1);
        if (n > 42)
           break;
        printf("%d\n",n);
    }
}

Such that the above code would print all numbers from 1 to 42. I thought of making yield change the address of getNext to the instruction after yield. But I cant figure out how I would save the context (registers/variables) since the stack would be ran over by the caller function.

Note:

I realize that the code above can be easily implemented by static variables, but that's not the point.

Shmoopy
  • 5,334
  • 4
  • 36
  • 72
  • However you do it, you won't be able to do it with the syntactic ease of C# (or whatever). This is what context variables and function pointers are for. – Joe Jul 04 '13 at 21:26
  • 1
    @Joe This is pure academic, just wondering what's the general idea. – Shmoopy Jul 04 '13 at 21:27
  • Have a look at `` maybe. – Kerrek SB Jul 04 '13 at 21:27
  • In general, `yield` style iterators need some restructuring of the code to turn it a state machine (unless you want to play stupid tricks with the stack, that is). Are you only interested in simple cases like the one in the question, where that isn't a concern? –  Jul 04 '13 at 21:28
  • Or perhaps [Boost.Coroutine](http://www.boost.org/doc/libs/1_54_0/libs/coroutine/doc/html/index.html) already has something useful, if you want to write in C++ (or just look at the source to learn some tricks). – Kerrek SB Jul 04 '13 at 21:29
  • @Shmoopy If I was going to do solve the problem that your example is trying to solve, I'd follow the iterator design pattern. I understand it's an academic question about yield. I don't think you're going to be able to end up with a construct that you can use in the style of `yield`. I'd love to be proved wrong. – Joe Jul 04 '13 at 21:29
  • If you want a state machine, you need a state variable - which is what a static variable is ideal for (static - state. It's even in the name). But you don't want to use a static... – Floris Jul 04 '13 at 21:29
  • @delnan Actually playing with the stack interests me. Maybe even keeping a different stack for functions that use yield – Shmoopy Jul 04 '13 at 21:31
  • Please let us know when you find an answer! More than one person would like to know. – Joe Jul 04 '13 at 21:33
  • @Floris The problem with a `static` variable is that you can't have more than one generator running at the same time (and if you want to restart it, things get ugly). It's conceptually awful, and in practice it only works for simple use cases. –  Jul 04 '13 at 21:33

3 Answers3

19

You can do so, even in portable C. Here is a crude example that works (gcc -Wall gen.c):

#include <stdbool.h>
#include <stdio.h>
#include <setjmp.h>

#define YIELD(func, n) if (! setjmp(func##_gen_jmp)) {  \
      func##_ret = n;                                   \
         longjmp(func##_caller_jmp, 1);                 \
  }


#define GENERATOR(ret, func, argt, argv)        \
  static jmp_buf func##_caller_jmp;             \
  static jmp_buf func##_gen_jmp;                \
  static bool func##_continue=false;            \
  static ret func##_ret;                        \
                                                \
  void func##__real(argt argv);                 \
                                                \
  ret func(argt argv) {                         \
    if (!func##_continue) {                     \
    func##_continue=true ;                      \
      if (! setjmp(func##_caller_jmp)) {        \
        func##__real(argv);                     \
      } else {                                  \
        return func##_ret;                      \
      }                                         \
    }                                           \
     else {                                     \
      longjmp(func##_gen_jmp,1);                \
    }                                           \
    return 0;                                   \
  }                                             \
  void func##__real(argt argv)



GENERATOR(int, getNext, int, n) {
  static int counter;

  counter = n;
    while (true) {
        counter = counter+1;
        YIELD(getNext, counter);
    }
}

int main() {
    while (true) {
      int n = getNext(1);
        if (n > 42)
           break;
        printf("%d\n",n);
    }
    return 0;
}
pennersr
  • 6,306
  • 1
  • 30
  • 37
2

What you're looking for is called "coroutines" and (at least not without loss of generality—see the other answer for a method for doing it in limited cases) is not possible in portable C. There are a number of tricks by which you can fake them; see http://en.wikipedia.org/wiki/Coroutine#Implementations_for_C for several.

torek
  • 448,244
  • 59
  • 642
  • 775
0

Here is a short version of something that was posted here a while back, but I can't find the original question or answer. I have reduced it to the shortest code that I can, and have added & changed comments. It generates squares from a range of numbers:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int sq_gen (int a, int b);

int sq_gen (int a, int b) {
    static int i, resume = 0;
    if (resume) goto resume; else resume = 1;
    printf("Initializing, start = %d, end = %d\n", a, b);
    for (i = a; i <= b; i += 1) {
        printf("Looping, counter = %d\n", i);
        return i * i;
        resume:;
    }
    resume = 0;
    return 0;
}

int main(int argc, char **argv) {
    int square;

    assert(argc == 3);
    while ((square = sq_gen((int)strtol(argv[1], NULL, 10), (int)strtol(argv[2], NULL, 10)))) {
        printf("main() square = %d\n", square);
    }

    return 0;
}

NOTE: start and end values are now command arguments.

m4r35n357
  • 111
  • 8