Is there a simple way?
Simple answer:
NO
Sorry, unless you narrow this down to some sort of very small subset of the functionality of closures, that's how it is.
I guess it depends on what your idea of "simple" is.
There are several implementations of Scheme which are designed to be integrated as an extension language for C programs. Link in a Scheme, write your closure in Scheme, and you're done.
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;
}