#include <iostream>
#include <memory>

#define CONCAT2(x, y) x ## y
#define CONCAT(x, y) CONCAT2(x, y)

#ifdef _WIN32

#define GET_LABEL_PTR(p, label)\
  {\
    void* _theLabelPointer;\
    __asm { mov [_theLabelPointer], offset label }\
    p = _theLabelPointer;\
  }

#define JUMP_TO_LABEL(p)\
  {\
    void* _theLabelPointer = p;\
    __asm { jmp _theLabelPointer }\
  }

#else // _WIN32

#define GET_LABEL_PTR(p, label) p = &&label;

#define JUMP_TO_LABEL(p) goto *p;

#endif // _WIN32

#define YIELD2(val,counter)\
  {\
    GET_LABEL_PTR(_p, CONCAT(YIELD,counter));\
    i = (val);\
    return i;\
    CONCAT(YIELD,counter): ;\
  }

#define YIELD(val) YIELD2(val,__COUNTER__)

#define RETURN { _ended = true; return i; }

#define DEF_ITER(ret_type, name, state_vars, code) \
  struct name \
  {\
    state_vars;\
    ret_type i;\
    bool _ended; void* _p;\
    name() : _ended(false), _p(0) {}\
    bool ended() const { return _ended; }\
    operator bool() { next(); return !_ended; }\
    ret_type next()\
    {\
      if (_ended) { throw(0); return i; }\
      if (_p)\
        JUMP_TO_LABEL(_p);\
      code;\
      \
       _ended = true;\
       return i;\
    }\
  };

// Test iterators

DEF_ITER(int, get57, ,
  YIELD(5);
  YIELD(7);
  RETURN;
  YIELD(42); // Should not happen
)

DEF_ITER(int, fibos,  int a;int b,
  a = 1;
  b = 1;
  for (;;)
  {
    YIELD(a);
    std::swap(a, b);
    b += a;
  }
)

DEF_ITER(int, range, int start;int end,
  while (start < end)
  {
    YIELD(start);
    ++start;
  }
)

DEF_ITER(int, range_recurse, int start; int end; std::unique_ptr<range_recurse> r,
  r = NULL;

  if (start >= end)
    RETURN;

  YIELD(start);
  r.reset(new range_recurse);
  r->start = start + 1;
  r->end = end;
  while (*r)
    YIELD(r->i);
)

DEF_ITER(int, n_fibos, int count; fibos f; range r,
  r.start = 0;
  r.end = count;
  while (r)
    YIELD(f.next());
)

int main(int argc, char* argv[])
{
  {
    std::cout << "*** 5 fibos" << std::endl;
    n_fibos s;
    s.count = 5;
    while (s)
      std::cout << s.i << std::endl;
  }

  {
    std::cout << "\n*** fibos < 20" << std::endl;
    fibos s;
    while (s && s.i < 20)
      std::cout << s.i << std::endl;
  }

  {
    std::cout << "\n*** 5 and 7" << std::endl;
    get57 s;
    while (s)
      std::cout << s.i << std::endl;
  }

  {
    std::cout << "\n*** 4..7" << std::endl;
    range s; s.start = 4; s.end = 8;
    while (s)
      std::cout << s.i << std::endl;
  }

  {
    std::cout << "\n*** 10..14" << std::endl;
    range_recurse s; s.start = 10; s.end = 15;
    while (s)
      std::cout << s.i << std::endl;
  }
  return 0;
}