Skip to content

Instantly share code, notes, and snippets.

@lpereira
Last active August 31, 2016 13:51
Show Gist options
  • Save lpereira/aef57db85f81d27598f3 to your computer and use it in GitHub Desktop.
Save lpereira/aef57db85f81d27598f3 to your computer and use it in GitHub Desktop.
/*
package main
import "fmt"
func main() {
fmt.Println("counting");
for i := 0; i < 10; i++ {
defer fmt.Println(i);
}
fmt.Println("done");
}
*/
#include <alloca.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum plain_type {
INT,
STRINGZ
/* STRINGPGM may be useful on an Arduino (constant strings in program
memory) */
/* COMPOSITE will have two varargs in Println: the Stringer,
and the ptr to the data */
};
void panic(void)
{
printf("Panic!\n");
exit(1);
}
/* fmt.c */
void fmt__Println(int nargs, ...)
{
va_list ap;
va_start(ap, nargs);
for (; nargs; nargs--) {
enum plain_type type = va_arg(ap, enum plain_type);
switch (type) {
case INT:
printf("%d", va_arg(ap, int));
break;
case STRINGZ:
printf("%s", va_arg(ap, const char *));
break;
}
}
va_end(ap);
putchar('\n');
}
/* defer-support.h */
struct defer_stack {
struct defer_stack *next;
void *ptr;
};
#define CONCAT_IMPL(a_, b_) a_ ## b_
#define CONCAT(a_, b_) CONCAT_IMPL(a_, b_)
/* The alloca() idea might be used as an alternative for the malloc() version *if* the current
* function is not being called as a goroutine, which might be implemented in a fashion similar
* to Simon Tatham's coroutines with a context struct. The computed gotos thing, though, is
* a lot nicer than creating function for each deferred statement. */
#define DEFER_NO_CLOSURE(...) \
do { \
struct defer_stack *defer_tmp = alloca(sizeof(*defer_tmp) __VA_ARGS__); \
defer_tmp->next = __defer_stack; \
defer_tmp->ptr = &&CONCAT(__defer_stmt, __COUNTER__); \
__defer_stack = defer_tmp; \
} while(0)
#define DEFER(closure_) \
do { \
DEFER_NO_CLOSURE(+ sizeof(closure_)); \
memcpy(__defer_stack + 1, &closure_, sizeof(closure_)); \
} while(0)
#define DISPATCH_DEFER() goto *__defer_stack->ptr;
#define NEXT_DEFER() \
do { \
__defer_stack = __defer_stack->next; \
DISPATCH_DEFER(); \
} while(0)
/* main.c */
struct main__defer0_closure {
int i;
};
int main(void)
{
/* Prologue */
struct defer_stack *__defer_stack = &(struct defer_stack) { .ptr = &&__defer_end };
/* End prologue */
/* Function */
fmt__Println(1, STRINGZ, "counting");
DEFER_NO_CLOSURE();
for (int i = 0; i < 10; i++) {
DEFER(((struct main__defer0_closure) { .i = i }));
}
fmt__Println(1, STRINGZ, "done");
DEFER_NO_CLOSURE();
/* Function end */
/* Epilogue */
/* Deferred statements */
DISPATCH_DEFER();
__defer_stmt0:
fmt__Println(1, STRINGZ, "finally");
NEXT_DEFER();
__defer_stmt1:
do {
struct main__defer0_closure *closure = (void *)(__defer_stack + 1);
fmt__Println(1, INT, closure->i);
NEXT_DEFER();
} while(0);
__defer_stmt2:
fmt__Println(1, STRINGZ, "oooh, goto works");
NEXT_DEFER();
__defer_end:
/* Unref */
/* -- nothing to collect -- */
/* End epilogue */
return 0;
}
/*
package main
import "fmt"
func main() {
fmt.Println("counting");
for i := 0; i < 10; i++ {
defer fmt.Println(i);
}
fmt.Println("done");
}
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum plain_type {
INT,
STRINGZ
/* STRINGPGM may be useful on an Arduino (constant strings in program
memory) */
};
void panic(void)
{
printf("Panic!\n");
exit(1);
}
/* fmt.c */
void fmt__Println(int nargs, ...)
{
/* This is pretty crude until interfaces are figured out */
va_list ap;
va_start(ap, nargs);
for (; nargs; nargs--) {
enum plain_type type = va_arg(ap, enum plain_type);
switch (type) {
case INT:
printf("%d", va_arg(ap, int));
break;
case STRINGZ:
printf("%s", va_arg(ap, const char *));
break;
}
}
va_end(ap);
putchar('\n');
}
/* defer.c */
/* The linked list here might be useful: http://250bpm.com/blog:56 */
/* Also, computed gotos instead of function calls */
struct defer_stack {
struct defer_stack *next;
void (*cb)(void *closure);
char closure[];
};
void defer__RunDeferredStatements(struct defer_stack *defer_stack)
{
while (defer_stack) {
struct defer_stack *tmp = defer_stack;
defer_stack->cb(defer_stack->closure);
defer_stack = defer_stack->next;
free(tmp);
}
}
struct defer_stack *defer__PushDeferred(struct defer_stack *defer_stack,
void (*cb)(void *data), void *closure, size_t sz)
{
struct defer_stack *node = malloc(sizeof(*node) + sz);
if (!node)
panic();
node->next = defer_stack;
node->cb = cb;
memcpy(node->closure, closure, sz);
return node;
}
/* main.c */
struct main__closure {
struct defer_stack *defer_stack;
};
static void main__defer(struct main__closure *main__closure)
{
defer__RunDeferredStatements(main__closure->defer_stack);
/* NB: unref refcounted variables from closure here as this shouldn't have a GC */
}
struct main__defer0_closure {
int i;
};
static void main__defer0(void *data)
{
struct main__defer0_closure *closure = data;
fmt__Println(1, INT, closure->i);
}
int main(void)
{
struct main__closure main__closure __attribute__((cleanup(main__defer))) = {};
fmt__Println(1, STRINGZ, "counting");
for (int i = 0; i < 10; i++) {
struct main__defer0_closure defer0_closure = {
.i = i
};
main__closure.defer_stack = defer__PushDeferred(
main__closure.defer_stack, &main__defer0,
&defer0_closure, sizeof(defer0_closure));
}
fmt__Println(1, STRINGZ, "done");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment