A number of ways to implement closures in C, in preparation of an upcoming blog post
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
struct closure { |
|
void (* call)(struct closure *); |
|
|
|
int x; |
|
}; |
|
|
|
void block(struct closure * env) { |
|
env->x += 1; |
|
printf ("block: x is %d\n", env->x); |
|
} |
|
|
|
struct closure * foo(int x) |
|
{ |
|
struct closure * closure = (struct closure *)malloc(sizeof(struct closure *)); |
|
closure->x = x; |
|
|
|
printf ("x is %d\n",closure->x); |
|
|
|
closure->call = █ |
|
|
|
return closure; |
|
} |
|
|
|
int main() { |
|
struct closure * c = foo(5); |
|
|
|
c->call(c); |
|
c->call(c); |
|
} |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
|
|
struct env { |
|
int x; |
|
}; |
|
|
|
struct closure { |
|
void (* call)(struct env *); |
|
struct env * env; |
|
}; |
|
|
|
void block(struct env * env) { |
|
env->x += 1; |
|
printf ("block: x is %d\n", env->x); |
|
} |
|
|
|
struct closure foo(int x) |
|
{ |
|
struct env * env = (struct env *)malloc(sizeof(struct env)); |
|
env->x = x; |
|
|
|
printf ("x is %d\n",env->x); |
|
|
|
struct closure closure; |
|
closure.env = env; |
|
closure.call = block; |
|
|
|
return closure; |
|
} |
|
|
|
int main() { |
|
struct closure c = foo(5); |
|
|
|
c.call(c.env); |
|
c.call(c.env); |
|
} |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
|
|
struct env { |
|
int x; |
|
}; |
|
|
|
struct closure { |
|
void (* call)(struct env *); |
|
struct env * env; |
|
}; |
|
|
|
void block(struct env * env) { |
|
env->x += 1; |
|
printf ("block: x is %d\n", env->x); |
|
} |
|
|
|
struct closure * foo(int x) |
|
{ |
|
struct env * env = (struct env *)malloc(sizeof(struct env)); |
|
env->x = x; |
|
|
|
printf ("x is %d\n",env->x); |
|
|
|
struct closure * closure = (struct closure *)malloc(sizeof(struct closure *)); |
|
closure->env = env; |
|
closure->call = block; |
|
|
|
return closure; |
|
} |
|
|
|
|
|
int main() { |
|
struct closure * c = foo(5); |
|
|
|
c->call(c->env); |
|
c->call(c->env); |
|
} |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <sys/mman.h> |
|
|
|
struct env { |
|
int x; |
|
}; |
|
|
|
struct __attribute__((packed)) thunk { |
|
unsigned char push; |
|
struct env * env_addr; |
|
unsigned char call; |
|
signed long call_offset; |
|
unsigned char add_esp[3]; |
|
unsigned char ret; |
|
}; |
|
|
|
struct thunk default_thunk = {0x68, 0, 0xe8, 0, {0x83, 0xc4, 0x04}, 0xc3}; |
|
|
|
typedef void (* cfunc)(); |
|
|
|
struct thunk * make_thunk(struct env * env, void * code) |
|
{ |
|
struct thunk * thunk = (struct thunk *)mmap(0,sizeof(struct thunk), PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
|
*thunk = default_thunk; |
|
thunk->env_addr = env; |
|
thunk->call_offset = code - (void *)&thunk->add_esp[0]; // Pretty! |
|
mprotect(thunk,sizeof(struct thunk), PROT_EXEC); |
|
return thunk; |
|
} |
|
|
|
|
|
void block(struct env * env) { |
|
env->x += 1; |
|
printf ("block: x is %d\n", env->x); |
|
} |
|
|
|
cfunc foo (int x) |
|
{ |
|
struct env * env = (struct env *)malloc(sizeof(struct env)); |
|
env->x = x; |
|
|
|
printf ("x is %d\n",env->x); |
|
|
|
return (cfunc)make_thunk(env,(void *)&block); |
|
} |
|
|
|
int main() { |
|
cfunc c = foo(5); |
|
|
|
c(); |
|
c(); |
|
} |
This comment has been minimized.
qqp commentedJan 31, 2014
https://gist.github.com/vidarh/259462#file-closures-thunks-c-L24
Shouldn't that be PROT_WRITE, not PROT_WRITE | PROT_EXEC?