Instantly share code, notes, and snippets.

Embed
What would you like to do?
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 = &block;
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();
}
@qqp

This comment has been minimized.

qqp commented Jan 31, 2014

https://gist.github.com/vidarh/259462#file-closures-thunks-c-L24

Shouldn't that be PROT_WRITE, not PROT_WRITE | PROT_EXEC?

@H2CO3

This comment has been minimized.

H2CO3 commented Feb 3, 2014

Thanks for this article. I'm reading it right now in order to implement closures in my own scripting language, Sparkling (here it is). Just one thing I noticed: you should not cast the return value of malloc(). Just write this: struct env *p = malloc(sizeof(*p));

@ljf10000

This comment has been minimized.

ljf10000 commented Apr 18, 2014

struct closure * closure = (struct closure *)malloc(sizeof(struct closure *));

should be below ???:
struct closure * closure = (struct closure *)malloc(sizeof(struct closure));

@kravemir

This comment has been minimized.

kravemir commented Sep 5, 2015

64-bit version? :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment