Это довольно широкий вопрос. По сути, замыкание - это указатель инструкций вместе с некоторым сохраненным контекстом, который необходим для правильного выполнения инструкций. Вы, конечно, можете создать что-то подобное в C, используя структуры и указатели функций.
Допустим, вы выражаете замыкание, которое принимает два целых числа и возвращает void в виде структуры:
typedef struct VoidClosureIntInt {
void (*fn)(int, int);
int first;
int second;
} VoidClosureIntInt;
и предположим, что у вас есть функция:
void Foo(int x, int y);
Теперь, чтобы создать замыкание, которое будет вызывать Foo (23, 42), вы должны сделать:
VoidClosureIntInt closure = {&Foo, 23, 42};
А затем, чтобы позже выполнить это закрытие, вы должны сделать:
(*closure.fn)(closure.first, closure.second);
Еще одна проблема: в большинстве случаев, когда вы используете замыкания, вы хотите передавать контекст за пределы жизненного цикла блока кода, в котором вы создаете замыкание. (Пример: вы передаете замыкание в функцию, которая выполняет некоторый асинхронный ввод-вывод и в конце концов вызовет ваше замыкание, когда этот ввод-вывод завершится) В таких случаях вы должны обязательно разместить свое закрытие в куче и удалить свое закрытие, когда закончите с ним. (См. Полный пример внизу).
Последнее замечание: здесь явно много механизмов, и это только для одного вида замыкания (функция, которая принимает два целочисленных аргумента и возвращает void). Когда я видел, как это делается в C, это часто делалось генератором кода, который создает механизм для многих различных типов замыканий. Вы также можете уменьшить количество шаблонов, поддерживая только те замыкания, которые принимают несколько (фиксированное число) аргументов void *, а затем вставляя типы в функции, используемые для реализации этих замыканий.
Если вы работаете в C ++, вы можете воспользоваться возможностями языка, чтобы сделать это гораздо более обобщенно и с гораздо меньшим набором текста. См. Boost.Function для примера.
Полный пример:
#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;
}