Skip to content

Instantly share code, notes, and snippets.

@christian-mann
Created July 17, 2015 03:08
Show Gist options
  • Save christian-mann/01df1a8b762939c06b0a to your computer and use it in GitHub Desktop.
Save christian-mann/01df1a8b762939c06b0a to your computer and use it in GitHub Desktop.
Lambdas in C
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef C_LAMBDA
#define C_LAMBDA
typedef struct {
void *addr;
void *val;
size_t size;
} CLOSURE_CLOSED_VAR;
typedef struct {
CLOSURE_CLOSED_VAR *closed;
int closed_len;
void *arguments;
size_t arguments_size;
jmp_buf ret_addr;
void *ret_val;
size_t ret_val_len;
} CLOSURE_CTX;
typedef struct {
jmp_buf entry;
CLOSURE_CTX *ctx;
} CLOSURE_HANDLE;
#define CLOSURE_CTX_CONSTRUCT(ReturnType, ArgumentType) ({ \
CLOSURE_CTX *ctx = (CLOSURE_CTX *) malloc(sizeof(CLOSURE_CTX)); \
ctx->closed = NULL; \
ctx->closed_len = 0; \
ctx->arguments = malloc(sizeof(ArgumentType)); \
ctx->arguments_size = sizeof(ArgumentType); \
ctx->ret_val = malloc(sizeof(ReturnType)); \
ctx->ret_val_len = sizeof(ReturnType); \
ctx; \
})
#define CLOSURE_ENCLOSE_VAR(ctx, name) { \
ctx->closed_len++; \
ctx->closed = (CLOSURE_CLOSED_VAR *) realloc(ctx->closed, ctx->closed_len * sizeof(CLOSURE_CLOSED_VAR)); \
ctx->closed[ctx->closed_len - 1].addr = &name; \
ctx->closed[ctx->closed_len - 1].val = name; \
ctx->closed[ctx->closed_len - 1].size = sizeof(name); \
}
// these arguments are really unfortunate, but I'm not sure how to declare a variable of a given type from macro
// especially if it's a function pointer e.g. int (*arg)(int) -- how do I get the name out of that?
#define CLOSURE_LAMBDA_START(ctx, type_arg, arg) { \
jmp_buf buf; \
int ret = setjmp(buf); \
if (ret != 0) { \
type_arg; \
ctx = (CLOSURE_CTX *) ret; \
for (int i = 0; i < ctx->closed_len; i++) { \
memcpy(ctx->closed[i].addr, &(ctx->closed[i].val), ctx->closed[i].size); \
} \
\
char *arg_ptr = (char *)ctx->arguments; \
memcpy(&arg, arg_ptr, sizeof(arg)); \
arg_ptr += sizeof(arg);
#define CLOSURE_RETURN(ctx, expr) \
* ( __typeof__ (expr) * ) ctx->ret_val = expr; \
longjmp(ctx->ret_addr, (int) &ctx);
#define CLOSURE_LAMBDA_END(ctx, handle) \
} \
memcpy(handle.entry, buf, sizeof(jmp_buf)); \
handle.ctx = ctx; \
}
#define CLOSURE_CALL(handle, arg) ({ \
* ( __typeof__(arg) *) handle.ctx->arguments = arg; \
jmp_buf back; \
if (setjmp(back) == 0) { \
memcpy(handle.ctx->ret_addr, back, sizeof(jmp_buf)); \
longjmp(handle.entry, (int) handle.ctx); \
} \
int retval = * ( __typeof__(retval) * ) handle.ctx->ret_val; \
retval; \
})
#endif
CLOSURE_HANDLE accumulator() {
int *sum = (int *) malloc(sizeof(int));
CLOSURE_CTX *ctx = CLOSURE_CTX_CONSTRUCT(int, int);
// only close-by-value is supported :(
CLOSURE_ENCLOSE_VAR(ctx, sum);
CLOSURE_HANDLE handle;
CLOSURE_LAMBDA_START(ctx, int addend, addend) {
*sum += addend;
CLOSURE_RETURN(ctx, *sum);
} CLOSURE_LAMBDA_END(ctx, handle);
return handle;
}
int main(int argc, char **argv) {
CLOSURE_HANDLE handle = accumulator();
int result = CLOSURE_CALL(handle, 3);
result = CLOSURE_CALL(handle, 5);
printf("%d\n", result);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment