Created
January 4, 2014 04:07
-
-
Save apparentlymart/8251535 to your computer and use it in GitHub Desktop.
Fat function pointers in C, to implement closures and object-bound methods
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 <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