Skip to content

Instantly share code, notes, and snippets.

@RicoP
Last active April 7, 2024 11:09
Show Gist options
  • Save RicoP/709150cdd5cda2669a8b3158626deeed to your computer and use it in GitHub Desktop.
Save RicoP/709150cdd5cda2669a8b3158626deeed to your computer and use it in GitHub Desktop.
/*
* 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;
#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;
}
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