Last active
April 7, 2024 11:09
-
-
Save RicoP/709150cdd5cda2669a8b3158626deeed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include "coroutines.h" | |
struct Generator { | |
float time; | |
// line == 0 -> startstate | |
// line == -1 -> endstate | |
int line; | |
Generator() : | |
time(0.0f), | |
line(0) { | |
} | |
}; | |
class CoroutineState { | |
float magic; | |
public: | |
int i; | |
CoroutineState() : | |
magic(42.0f), | |
i(0) { | |
} | |
void print(int value) const { | |
std::cout << value << std::endl; | |
} | |
void print(const char * str) const { | |
std::cout << str << std::endl; | |
} | |
float getMagicValue() const { | |
return magic; | |
} | |
void setMagicValue(float value) { | |
magic = value; | |
} | |
}; | |
static bool coroutine(Generator & generator, void * host) { | |
CoroutineState & state = *static_cast<CoroutineState *>(host); | |
YIELD_START { | |
for (state.i = 0; state.i < 3; state.i++) { | |
YIELD_RETURN_VOID; | |
state.print(state.i); | |
} | |
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 | |
} | |
int main() { | |
Generator generator; | |
CoroutineState state; | |
while (coroutine(generator, &state)) { | |
std::cout | |
<< " Current line: " << generator.line | |
<< ". Wait for " << generator.time << " seconds!" << std::endl; | |
} | |
return 0; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Current line: 52. Wait for 0 seconds! | |
0 | |
Current line: 52. Wait for 0 seconds! | |
1 | |
Current line: 52. Wait for 0 seconds! | |
2 | |
Current line: 56. Wait for 1 seconds! | |
one | |
Current line: 59. Wait for 1 seconds! | |
two | |
Current line: 62. Wait for 1 seconds! | |
done! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment