Skip to content

Instantly share code, notes, and snippets.

@apparentlymart
Created January 4, 2014 04:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apparentlymart/8251535 to your computer and use it in GitHub Desktop.
Save apparentlymart/8251535 to your computer and use it in GitHub Desktop.
Fat function pointers in C, to implement closures and object-bound methods
#include <stdio.h>
// we'd generate one of these for each distinct function signature
typedef struct {
void (*func)();
void *env;
} Callable;
// we'd generate an environment struct for each function that needs one
typedef struct {
int x;
int y;
} Environment1;
typedef struct {
int r;
} Environment2;
// Callback functions will always take an initial void * parameter for the environment.
// If this were a bound method then this param could be the object it's bound to.
void callback1(void *env) {
printf(
"callback1 (x=%i, y=%i)\n",
((Environment1*)env)->x,
((Environment1*)env)->y
);
}
void callback2(void *env) {
printf(
"callback2 (r=%i)\n",
((Environment2*)env)->r
);
}
void callback3(void *env) {
printf("callback3\n");
}
// We can make an array containing a mixture of different functions as long as they
// all have the same signature.
Callable callbacks[3];
// Helper function to call a callable. Inlined because it avoids copying the Callable object.
// On AVR after optimization this ends up being 4 operations to load registers and then one
// indirect call, for a total of 18 bytes. Two of those operations are loading the Z register
// to prepare for the indirect call, so this overhead is unavoidable for any sort of indirect
// calling.
inline void call_callable(Callable callable) {
callable.func(callable.env);
}
void main(int argc, char **argv) {
callbacks[0].func = callback1;
callbacks[2].func = callback2;
Environment1 env1 = {10, 11};
Environment1 env2a = {12, 13};
Environment2 env2b = {14};
callbacks[0].env = (void*)&env1;
if (argc == 1) {
callbacks[1].func = callback1;
callbacks[1].env = (void*)&env2a;
}
else {
callbacks[1].func = callback2;
callbacks[1].env = (void*)&env2b;
}
// this one ignores its environment, so we don't need to set it
callbacks[2].func = callback3;
// Now we can call all of the callbacks the same way.
call_callable(callbacks[0]);
call_callable(callbacks[1]);
call_callable(callbacks[2]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment