#ifndef _CORO_H_ #define _CORO_H_ #include // Basic usage example: // Define a specific coroutine struct holding all your variables and return values (variables defined normally within the function will be reset): // typedef struct { // Coro coro; // int x_pos, y_pos; // } ExampleCoro; // // Define a specific coroutine function taking our custom type as a parameter: // void example(ExampleCoro *c) // CORO_BEGIN(&c->coro) // The coroutine initialization simply takes a reference to the core coroutine struct // // Draw the object for a total of 1s // CORO_BEGIN_REPEAT(1.0) // // Rapidly alternate between states A and B displaying each for 0.1s // CORO_BEGIN_REPEAT(0.1) // DrawThingA(c->x_pos, c->y_pos); // CORO_END_REPEAT() // CORO_BEGIN_REPEAT(0.1) // DrawThingB(c->x_pos, c->y_pos); // CORO_END_REPEAT() // CORO_END_REPEAT() // // Print a message // printf("Dying in 1s\n"); // CORO_WAIT(1.0) // // Print another message after waiting for 1s // printf("XD\n"); // // Done; c->done is now set to true and the coroutine will no longer run any code on subsequent calls // CORO_END() // // Initialization (zero value is ready-to-use): // ExampleCoro coro; // coro = (ExampleCoro){0}; // // Each frame: // example(&coro) // coro.time += delta_time; #ifndef CORO_MAX_DEPTH #define CORO_MAX_DEPTH 8 #endif #ifndef CORO_FATAL #include #include #define CORO_FATAL(...) { fprintf(stderr, "[FATAL] Coro: "__VA_ARGS__); abort(); } #endif #define CORO_BEGIN(_coro) \ { \ Coro *_coro_instance = _coro; \ int _coro_goto_line = 0; \ if (_coro_instance->done) \ return; \ if (_coro_instance->stack_size > 0 && !_coro_instance->stack[_coro_instance->stack_size-1].repeat) { \ if (_coro_instance->time < _coro_instance->stack[_coro_instance->stack_size-1].timeout) \ return; \ } \ if (_coro_instance->stack_size > 0) \ _coro_goto_line = _coro_instance->stack[_coro_instance->stack_size-1].line; \ switch (_coro_goto_line) { \ case 0: #define CORO_END() \ if (_coro_instance->stack_size != 0) { CORO_FATAL("programmer error in %s:%d: CORO_END() called without closing all loops\n", __FILE__, __LINE__); } \ _coro_instance->done = true; \ }} // Do nothing until the specified amount of time has passed #define CORO_WAIT(_time) \ if (_coro_instance->stack_size+1 > CORO_MAX_DEPTH) { CORO_FATAL("programmer error in %s:%d: maximum depth of %d exceeded\n", __FILE__, __LINE__, CORO_MAX_DEPTH); } \ _coro_instance->stack_size++; \ _coro_instance->stack[_coro_instance->stack_size-1].repeat = false; \ _coro_instance->stack[_coro_instance->stack_size-1].timeout = _coro_instance->time + _time; \ case __LINE__: _coro_instance->stack[_coro_instance->stack_size-1].line = __LINE__; \ if (_coro_instance->time < _coro_instance->stack[_coro_instance->stack_size-1].timeout) \ return; \ else \ _coro_instance->stack_size--; // Repeat a piece of code each frame until the specified amount of time has passed #define CORO_BEGIN_REPEAT(_time) \ if (_coro_instance->stack_size+1 > CORO_MAX_DEPTH) { CORO_FATAL("programmer error in %s:%d: maximum depth of %d exceeded\n", __FILE__, __LINE__, CORO_MAX_DEPTH); } \ _coro_instance->stack_size++; \ _coro_instance->stack[_coro_instance->stack_size-1].repeat = true; \ _coro_instance->stack[_coro_instance->stack_size-1].timeout = _coro_instance->time + _time; \ case __LINE__: _coro_instance->stack[_coro_instance->stack_size-1].line = __LINE__; #define CORO_END_REPEAT() \ if (_coro_instance->stack_size == 0) { CORO_FATAL("programmer error in %s:%d: CORO_END_REPEAT() called without matching CORO_BEGIN_REPEAT()\n", __FILE__, __LINE__); } \ if (_coro_instance->time < _coro_instance->stack[_coro_instance->stack_size-1].timeout) \ return; \ else \ _coro_instance->stack_size--; typedef struct { struct { unsigned int line; // line to return to for wait/repeat bool repeat; // whether this is a repeat or simply a wait double timeout; // when the repeat/wait is over } stack[CORO_MAX_DEPTH]; // stack for keeping track of waits and repeats unsigned int stack_size; double time; // current time; meaning and unit defined by user bool done; // set to true when coroutine is finished executing } Coro; #endif /* !_CORO_H_ */