Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing yield in C

Tags:

yield

c

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.

like image 880
Shmoopy Avatar asked Jul 04 '13 21:07

Shmoopy


People also ask

What is yield () in C?

In computer science, yield is an action that occurs in a computer program during multithreading, of forcing a processor to relinquish control of the current running thread, and sending it to the end of the running queue, of the same scheduling priority.

What is the use of yield keyword?

The yield keyword is use to do custom stateful iteration over a collection. The yield keyword tells the compiler that the method in which it appears is an iterator block. yield return <expression>; yield break; The yield return statement returns one element at a time.

How does yield work?

Yield is a return measure for an investment over a set period of time, expressed as a percentage. Yield includes price increases as well as any dividends paid, calculated as the net realized return divided by the principal amount (i.e. amount invested).


2 Answers

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;
}
like image 80
pennersr Avatar answered Oct 18 '22 15:10

pennersr


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.

like image 2
torek Avatar answered Oct 18 '22 14:10

torek