4 ms·
I guess it's just not very well known that C has functions which can also be passed as values. Sure, C has function pointers. But it doesn't have closures: the
by timrobinson 16y ago
I guess it's just not very well known that C has functions which can also be passed as values.
Sure, C has function pointers. But it doesn't have closures: the things you can do with just a function pointer are fairly limited.
The callback syntax in the post should really be:
typedef void *(ListMapCallback)(void *item, void *state);
...which gets ugly quickly (passing your closure state around as a void*) and doesn't compose well (passing your closure state into your implementations of List_map_, List_fold_, etc.)
- yason 16y agoIt's not necessarily just ugly but plain idiomatic C. It's just C: a function pointer + userdata pointer. All hooks, callbacks, etc. use this approach because that's what a closure is on a low enough level. And in C it looks like void()(void userdata).
- timrobinson 16y agoMy "ugly" comment comes from spending my career programming against APIs like this :) typedef BOOL (* WNDENUMPROC) (HWND, LPARAM); BOOL WINAPI EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
- gjm11 16y agoIt's not only that C doesn't have closures; it doesn't even have anonymous functions. Here's a little Lisp expression that doesn't need closures (mapcar #'(lambda (x) (* x x)) some-list) which squares all the elements of the list. To do that in C you need some abomination like /* Somewhere outside the function where you actually need this */ int square(x) { return x*x; } /* ... */ List *r = List_new(); LIST_FOREACH(self, i, v, List_append_(r, square(v));); Not to mention that this sort of thing tends to get ugly without automatic memory management.
- JoachimSchipper 16y agoThe proper way to do this is (simplified from BSD sys/queue.h): #define LIST_FOREACH(var, head) \ for ((var) = (head); (var) != NULL; (var) = (var)->next); LIST_FOREACH(list_p, list_head) { ... } You may also occasionally want to use (C99) variadic macros (gcc seems to need -std=c99): #define LIST_MAP(my_list, f, ...) \ for (struct list *n = my_list; n != NULL; n = n->next) \ f(__VA_ARGS__, n) void list_print(const char *format, struct list *n) { printf(format, n->data); } int main(void) { struct list n3 = {NULL, 3}, n2 = {&n3, 2}, n1 = {&n2, 1}; LIST_MAP(&n1, list_print, "%d\n"); }
- regularfry 16y agoThere was a spectacular hack involving gcc and dynamic linking which let you do closures in C posted to reddit a few months ago. Naturally I can't find it - anyone save the link?