Skip to content

Instantly share code, notes, and snippets.

@vidarh
Created December 18, 2009 12:10
Show Gist options
  • Star 52 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save vidarh/259462 to your computer and use it in GitHub Desktop.
Save vidarh/259462 to your computer and use it in GitHub Desktop.
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
Copy link

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
Copy link

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
Copy link

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

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

@kravemir
Copy link

kravemir commented Sep 5, 2015

64-bit version? :)

@Hywan
Copy link

Hywan commented Mar 11, 2019

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