|
/* |
|
* Coroutines. |
|
* |
|
* To make a (fake) coroutine we use these macros. A coroutine method must be static, |
|
* return bool and needs a parameter (Generator & generator). |
|
* |
|
* Generator must follow this schema: |
|
* struct Generator { |
|
* int line; |
|
* float time; |
|
* }; |
|
* |
|
* The return value indicates if the coroutine is still active. |
|
* generator.line indicates at which line the coroutine currently is. |
|
* generator.line == 0 means the coroutine is at the beginning. |
|
* generator.line == -1 means the coroutine is done. |
|
* |
|
* YIELD_START and YIELD_END must mark the start and end of the coroutine. |
|
* YIELD_RETURN_VOID interrupts the coroutine and continues it at the same line. |
|
* YIELD_RETURN_TIME does the same as YIELD_RETURN_VOID but sets the time value of generator first. |
|
* YIELD_BREAK stops the coroutine |
|
* |
|
* Local variables should be avoided because their value will be lost after re-entry. |
|
* You might have local variables before YIELD_START. |
|
* |
|
* NOTE: Inside a coroutine we are not allowed to use the |
|
* switch statement because it messes with the macros. |
|
* |
|
* example: |
|
* |
|
* static bool coroutine(Generator & generator, void * host) { |
|
* CoroutineState & state = *static_cast<CoroutineState *>(host); |
|
* |
|
* YIELD_START { |
|
* for (state.i = 0; state.i < 3; state.i++) { |
|
* state.print(state.i); |
|
* YIELD_RETURN_VOID; |
|
* } |
|
* |
|
* YIELD_RETURN_TIME(1.0f); |
|
* |
|
* state.print("one"); |
|
* YIELD_RETURN_TIME(1.0f); |
|
* |
|
* state.print("two"); |
|
* YIELD_RETURN_TIME(1.0f); |
|
* |
|
* // local variables are ok as long declaration |
|
* // and use is not crossing YIELD_* |
|
* float foo = state.getMagicValue() * 1.7f; |
|
* |
|
* state.setMagicValue(foo); |
|
* |
|
* state.print("done!"); |
|
* } |
|
* YIELD_END |
|
* } |
|
*/ |
|
|
|
// Wrapper for multi-line macros to avoid warnings about constant expressions |
|
//lint -emacro(717, MULTI_LINE_MACRO_END) do ... while(0) |
|
#ifdef _MSC_VER |
|
# define MULTI_LINE_MACRO_BEGIN do { |
|
# define MULTI_LINE_MACRO_END \ |
|
__pragma(warning(push)) \ |
|
__pragma(warning(disable: 4127)) /* conditional expression is constant */ \ |
|
} while (false) \ |
|
__pragma(warning(pop)) |
|
#else |
|
# define MULTI_LINE_MACRO_BEGIN do { |
|
# define MULTI_LINE_MACRO_END } while (false) |
|
#endif |
|
|
|
//lint -emacro(9055, YIELD_RETURN_VOID, YIELD_RETURN_TIME, YIELD_BREAK) Most closely enclosing compound statement of a case/default is not the body of a switch |
|
//lint -emacro(646, YIELD_RETURN_VOID, YIELD_RETURN_TIME, YIELD_BREAK) case/default within do loop; may have been misplaced |
|
//-V:YIELD_RETURN_VOID:612 |
|
//-V:YIELD_RETURN_TIME:612 |
|
//-V:YIELD_BREAK:612 |
|
|
|
#define YIELD_START switch (generator.line) { case 0: |
|
|
|
#define YIELD_RETURN_VOID \ |
|
MULTI_LINE_MACRO_BEGIN \ |
|
generator.line = __LINE__; \ |
|
return true; \ |
|
case __LINE__: {}; \ |
|
MULTI_LINE_MACRO_END |
|
|
|
#define YIELD_RETURN_TIME(TIME) \ |
|
MULTI_LINE_MACRO_BEGIN \ |
|
generator.time = TIME; \ |
|
YIELD_RETURN_VOID; \ |
|
MULTI_LINE_MACRO_END |
|
|
|
#define YIELD_BREAK \ |
|
MULTI_LINE_MACRO_BEGIN \ |
|
generator.line = -1; \ |
|
return false; \ |
|
MULTI_LINE_MACRO_END |
|
|
|
#define YIELD_END } YIELD_BREAK; |