Created
July 17, 2015 03:08
-
-
Save christian-mann/01df1a8b762939c06b0a to your computer and use it in GitHub Desktop.
Lambdas in C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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