Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to emulate closures in c

Tags:

c

closures

Is there a simple way?

like image 626
Pierreten Avatar asked Mar 20 '10 00:03

Pierreten


People also ask

Is there closure in C?

Although C was created two decades after Lisp, it nonetheless lacks support for closures.

How does a function create a closure?

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.


1 Answers

That's a pretty broad question. Fundamentally a closure is an instruction pointer along with some stored context that's required to execute the instructions in the right way. You can certainly throw something like this together in C using structs and function pointers.

Let's say you express a closure that takes two ints and returns void as a struct:

typedef struct VoidClosureIntInt {
  void (*fn)(int, int);
  int first;
  int second;
} VoidClosureIntInt;

and suppose you have a function:

void Foo(int x, int y);

Now, to create a closure that will invoke Foo(23, 42), you would do:

VoidClosureIntInt closure = {&Foo, 23, 42};

And then to later execute that closure, you would do:

(*closure.fn)(closure.first, closure.second);

One more wrinkle: most of the time when you're using closures, you want to pass context around beyond the lifetime of the code block in which you create the closure. (Example: you're passing the closure into a function that does some asynchronous I/O and will eventually call your closure when that I/O is finished). In such cases, you must be sure to allocate your closure on the heap, and to delete your closure when you're finished with it. (See complete example at the bottom).

One final note: there's obviously a lot of machinery here, and it's just for one kind of closure (a function that takes two integer args and returns void). When I've seen this done in C it's often been done by a code generator that creates machinery for many different kinds of closures. You can also reduce the amount of boilerplate by only supporting closures that take some (fixed number of) void* arguments, and then typecasting within the functions you're using to implement those closures.

If you're in C++, you can take advantage of language features to do this much more generically and with much less typing. See Boost.Function for an example.

Full example:

#include <stdio.h>
#include <stdlib.h>

// Closure support.

typedef struct VoidClosureIntInt {
  void (*fn)(int, int);
  int first;
  int second;
} VoidClosureIntInt;

// The returned closure should be run via RunAndDeleteClosure().
VoidClosureIntInt* NewClosure(void (*fn)(int, int), int first, int second) {
  VoidClosureIntInt* closure = malloc(sizeof(*closure));
  closure->fn = fn;
  closure->first = first;
  closure->second = second;
  return closure;
}

void RunAndDeleteClosure(VoidClosureIntInt* closure) {
  (*closure->fn)(closure->first, closure->second);
  free(closure);
}


// Example use.

void Foo(int x, int y) {
  printf("x=%d\ny=%d\n", x, y);
}

// We take memory ownership of closure.
void SomeAsynchronousFunction(VoidClosureIntInt* closure) {
  RunAndDeleteClosure(closure);
}

int main(int argc, char** argv) {
  VoidClosureIntInt* closure = NewClosure(&Foo, 23, 42);
  SomeAsynchronousFunction(closure);
  return 0;
}
like image 70
Will Robinson Avatar answered Sep 27 '22 17:09

Will Robinson