如何在c中模拟闭包

发布于 2024-08-25 19:53:05 字数 15 浏览 0 评论 0原文

有简单的方法吗?

Is there a simple way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

勿忘心安 2024-09-01 19:53:05

这是一个相当广泛的问题。从根本上讲,闭包是一个指令指针以及一些以正确方式执行指令所需的存储上下文。您当然可以在 C 中使用结构体和函数指针将类似的东西组合在一起。

假设您表达了一个采用两个 int 并返回 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);

还有一个问题:大多数时候,当你使用闭包时,你希望在创建闭包的代码块的生命周期之外传递上下文。 (例如:您将闭包传递给执行一些异步 I/O 的函数,并最终在该 I/O 完成时调用您的闭包)。在这种情况下,您必须确保在堆上分配闭包,并在使用完毕后将其删除。 (请参阅底部的完整示例)。

最后一点:这里显然有很多机制,而且它只是用于一种闭包(一个接受两个整数参数并返回 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;
}

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;
}
奈何桥上唱咆哮 2024-09-01 19:53:05

简单的答案:

抱歉,除非您将其范围缩小到闭包功能的某种非常小的子集,否则就是这样。

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.

一页 2024-09-01 19:53:05

我想这取决于你对“简单”的理解。

Scheme 有多种实现,它们被设计为集成为 C 程序的扩展语言。在Scheme 中链接,在Scheme 中编写您的闭包,然后就完成了。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文