Как эмулировать замыкания в c - PullRequest
7 голосов
/ 20 марта 2010

Есть ли простой способ?

Ответы [ 3 ]

14 голосов
/ 20 марта 2010

Это довольно широкий вопрос. По сути, замыкание - это указатель инструкций вместе с некоторым сохраненным контекстом, который необходим для правильного выполнения инструкций. Вы, конечно, можете создать что-то подобное в 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;
}
0 голосов
/ 20 марта 2010

Полагаю, это зависит от того, каково ваше представление о "простом".

Существует несколько реализаций Scheme, которые предназначены для интеграции в качестве языка расширения для программ на Си. Сделайте ссылку в Схеме, напишите свое закрытие в Схеме, и все готово.

0 голосов
/ 20 марта 2010

Простой ответ:

нет

NO

Извините, если вы не сузите это до какого-то очень небольшого подмножества функциональных возможностей замыканий, вот как это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...