coro/coro.h

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_ */