107 lines
4.4 KiB
C
107 lines
4.4 KiB
C
#ifndef _CORO_H_
|
|
#define _CORO_H_
|
|
|
|
#include <stdbool.h>
|
|
|
|
// 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#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_ */
|